>script>var a=window.devsite||{};window.devsite=a;a.readyCallbacks=[];window.devsite.readyCallbacks=a.readyCallbacks;a.ready=function(b){a.readyCallbacks.push(b)};window.devsite.ready=a.ready; Getting Started | Android Developers Skip to content

Most visited

Recently visited

navigation

Getting Started

Welcome to Training for Android developers. Here you'll find training classes that describe how to accomplish a specific task with code samples you can re-use in your app. Classes are organized into several groups you can see at the top-level of the left navigation.

The first training guides below teach you the essentials for Android app development. If you're a new Android app developer, you should complete each of these classes in order.

Various online video courses are also available if you'd prefer an interactive video experience.

Check out this trailer for a course about the fundamentals of Android development on Udacity.

Start the video course

Online video courses

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Building Your First App | Android Developers Skip to content

Most visited

Recently visited

navigation

Building Your First App

Dependencies

Welcome to Android application development!

This class teaches you how to build your first Android app. You’ll learn how to create an Android project and run a debuggable version of the app. You'll also learn some fundamentals of Android app design, including how to build a simple user interface and handle user input.

Set Up Your Environment

Before you start this class, be sure you have your development environment set up. You need to:

  1. Download Android Studio.
  2. Download the latest SDK tools and platforms using the SDK Manager.

Note: Although most of this training class expects that you're using Android Studio, some procedures include alternative instructions for using the SDK tools from the command line instead.

This class uses a tutorial format to create a small Android app that teaches you some fundamental concepts about Android development, so it's important that you follow each step.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Supporting Different Devices | Android Developers Skip to content

Most visited

Recently visited

navigation

Supporting Different Devices

Dependencies and prerequisites

  • Android 1.6 or higher

You should also read

Android devices come in many shapes and sizes all around the world. With a wide range of device types, you have an opportunity to reach a huge audience with your app. In order to be as successful as possible on Android, your app needs to adapt to various device configurations. Some of the important variations that you should consider include different languages, screen sizes, and versions of the Android platform.

This class teaches you how to use basic platform features that leverage alternative resources and other features so your app can provide an optimized user experience on a variety of Android-compatible devices, using a single application package (APK).

Lessons

Supporting Different Languages
Learn how to support multiple languages with alternative string resources.
Supporting Different Screens
Learn how to optimize the user experience for different screen sizes and densities.
Supporting Different Platform Versions
Learn how to use APIs available in new versions of Android while continuing to support older versions of Android.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Supporting Different Languages | Android Developers Skip to content

Most visited

Recently visited

navigation

Supporting Different Languages

It’s always a good practice to extract UI strings from your app code and keep them in an external file. Android makes this easy with a resources directory in each Android project.

If you created your project using the Android SDK Tools (read Creating an Android Project), the tools create a res/ directory in the top level of the project. Within this res/ directory are subdirectories for various resource types. There are also a few default files such as res/values/strings.xml, which holds your string values.

Create Locale Directories and String Files

To add support for more languages, create additional values directories inside res/ that include a hyphen and the ISO language code at the end of the directory name. For example, values-es/ is the directory containing simple resources for the Locales with the language code "es". Android loads the appropriate resources according to the locale settings of the device at run time. For more information, see Providing Alternative Resources.

Once you’ve decided on the languages you will support, create the resource subdirectories and string resource files. For example:

MyProject/
    res/
       values/
           strings.xml
       values-es/
           strings.xml
       values-fr/
           strings.xml

Add the string values for each locale into the appropriate file.

At runtime, the Android system uses the appropriate set of string resources based on the locale currently set for the user's device.

For example, the following are some different string resource files for different languages.

English (default locale), /values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="title">My Application</string>
    <string name="hello_world">Hello World!</string>
</resources>

Spanish, /values-es/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="title">Mi Aplicación</string>
    <string name="hello_world">Hola Mundo!</string>
</resources>

French, /values-fr/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="title">Mon Application</string>
    <string name="hello_world">Bonjour le monde !</string>
</resources>

Note: You can use the locale qualifier (or any configuration qualifer) on any resource type, such as if you want to provide localized versions of your bitmap drawable. For more information, see Localization.

Use the String Resources

You can reference your string resources in your source code and other XML files using the resource name defined by the <string> element's name attribute.

In your source code, you can refer to a string resource with the syntax R.string.<string_name>. There are a variety of methods that accept a string resource this way.

For example:

// Get a string resource from your app's Resources
String hello = getResources().getString(R.string.hello_world);

// Or supply a string resource to a method that requires a string
TextView textView = new TextView(this);
textView.setText(R.string.hello_world);

In other XML files, you can refer to a string resource with the syntax @string/<string_name> whenever the XML attribute accepts a string value.

For example:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" />
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Supporting Different Screens | Android Developers Skip to content

Most visited

Recently visited

navigation

Supporting Different Screens

Android categorizes device screens using two general properties: size and density. You should expect that your app will be installed on devices with screens that range in both size and density. As such, you should include some alternative resources that optimize your app’s appearance for different screen sizes and densities.

  • There are four generalized sizes: small, normal, large, xlarge
  • And four generalized densities: low (ldpi), medium (mdpi), high (hdpi), extra high (xhdpi)

To declare different layouts and bitmaps you'd like to use for different screens, you must place these alternative resources in separate directories, similar to how you do for different language strings.

Also be aware that the screens orientation (landscape or portrait) is considered a variation of screen size, so many apps should revise the layout to optimize the user experience in each orientation.

Create Different Layouts

To optimize your user experience on different screen sizes, you should create a unique layout XML file for each screen size you want to support. Each layout should be saved into the appropriate resources directory, named with a -<screen_size> suffix. For example, a unique layout for large screens should be saved under res/layout-large/.

Note: Android automatically scales your layout in order to properly fit the screen. Thus, your layouts for different screen sizes don't need to worry about the absolute size of UI elements but instead focus on the layout structure that affects the user experience (such as the size or position of important views relative to sibling views).

For example, this project includes a default layout and an alternative layout for large screens:

MyProject/
    res/
        layout/
            main.xml
        layout-large/
            main.xml

The file names must be exactly the same, but their contents are different in order to provide an optimized UI for the corresponding screen size.

Simply reference the layout file in your app as usual:

@Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
}

The system loads the layout file from the appropriate layout directory based on screen size of the device on which your app is running. More information about how Android selects the appropriate resource is available in the Providing Resources guide.

As another example, here's a project with an alternative layout for landscape orientation:

MyProject/
    res/
        layout/
            main.xml
        layout-land/
            main.xml

By default, the layout/main.xml file is used for portrait orientation.

If you want to provide a special layout for landscape, including while on large screens, then you need to use both the large and land qualifier:

MyProject/
    res/
        layout/              # default (portrait)
            main.xml
        layout-land/         # landscape
            main.xml
        layout-large/        # large (portrait)
            main.xml
        layout-large-land/   # large landscape
            main.xml

Note: Android 3.2 and above supports an advanced method of defining screen sizes that allows you to specify resources for screen sizes based on the minimum width and height in terms of density-independent pixels. This lesson does not cover this new technique. For more information, read Designing for Multiple Screens.

Create Different Bitmaps

You should always provide bitmap resources that are properly scaled to each of the generalized density buckets: low, medium, high and extra-high density. This helps you achieve good graphical quality and performance on all screen densities.

To generate these images, you should start with your raw resource in vector format and generate the images for each density using the following size scale:

  • xhdpi: 2.0
  • hdpi: 1.5
  • mdpi: 1.0 (baseline)
  • ldpi: 0.75

This means that if you generate a 200x200 image for xhdpi devices, you should generate the same resource in 150x150 for hdpi, 100x100 for mdpi, and 75x75 for ldpi devices.

Then, place the files in the appropriate drawable resource directory:

MyProject/
    res/
        drawable-xhdpi/
            awesomeimage.png
        drawable-hdpi/
            awesomeimage.png
        drawable-mdpi/
            awesomeimage.png
        drawable-ldpi/
            awesomeimage.png

Any time you reference @drawable/awesomeimage, the system selects the appropriate bitmap based on the screen's density.

Note: Low-density (ldpi) resources aren’t always necessary. When you provide hdpi assets, the system scales them down by one half to properly fit ldpi screens.

For more tips and guidelines about creating icon assets for your app, see the Iconography design guide.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Supporting Different Platform Versions | Android Developers Skip to content

Most visited

Recently visited

navigation

Supporting Different Platform Versions

While the latest versions of Android often provide great APIs for your app, you should continue to support older versions of Android until more devices get updated. This lesson shows you how to take advantage of the latest APIs while continuing to support older versions as well.

The dashboard for Platform Versions is updated regularly to show the distribution of active devices running each version of Android, based on the number of devices that visit the Google Play Store. Generally, it’s a good practice to support about 90% of the active devices, while targeting your app to the latest version.

Tip: In order to provide the best features and functionality across several Android versions, you should use the Android Support Library in your app, which allows you to use several recent platform APIs on older versions.

Specify Minimum and Target API Levels

The AndroidManifest.xml file describes details about your app and identifies which versions of Android it supports. Specifically, the minSdkVersion and targetSdkVersion attributes for the <uses-sdk> element identify the lowest API level with which your app is compatible and the highest API level against which you’ve designed and tested your app.

For example:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" />
    ...
</manifest>

As new versions of Android are released, some style and behaviors may change. To allow your app to take advantage of these changes and ensure that your app fits the style of each user's device, you should set the targetSdkVersion value to match the latest Android version available.

Check System Version at Runtime

Android provides a unique code for each platform version in the Build constants class. Use these codes within your app to build conditions that ensure the code that depends on higher API levels is executed only when those APIs are available on the system.

private void setUpActionBar() {
    // Make sure we're running on Honeycomb or higher to use ActionBar APIs
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        ActionBar actionBar = getActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
    }
}

Note: When parsing XML resources, Android ignores XML attributes that aren’t supported by the current device. So you can safely use XML attributes that are only supported by newer versions without worrying about older versions breaking when they encounter that code. For example, if you set the targetSdkVersion="11", your app includes the ActionBar by default on Android 3.0 and higher. To then add menu items to the action bar, you need to set android:showAsAction="ifRoom" in your menu resource XML. It's safe to do this in a cross-version XML file, because the older versions of Android simply ignore the showAsAction attribute (that is, you do not need a separate version in res/menu-v11/).

Use Platform Styles and Themes

Android provides user experience themes that give apps the look and feel of the underlying operating system. These themes can be applied to your app within the manifest file. By using these built in styles and themes, your app will naturally follow the latest look and feel of Android with each new release.

To make your activity look like a dialog box:

<activity android:theme="@android:style/Theme.Dialog">

To make your activity have a transparent background:

<activity android:theme="@android:style/Theme.Translucent">

To apply your own custom theme defined in /res/values/styles.xml:

<activity android:theme="@style/CustomTheme">

To apply a theme to your entire app (all activities), add the android:theme attribute to the <application> element:

<application android:theme="@style/CustomTheme">

For more about creating and using themes, read the Styles and Themes guide.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Managing the Activity Lifecycle | Android Developers Skip to content

Most visited

Recently visited

navigation

Managing the Activity Lifecycle

Dependencies and prerequisites

You should also read

Try it out

Download the demo

ActivityLifecycle.zip

As a user navigates through, out of, and back to your app, the Activity instances in your app transition between different states in their lifecycle. For instance, when your activity starts for the first time, it comes to the foreground of the system and receives user focus. During this process, the Android system calls a series of lifecycle methods on the activity in which you set up the user interface and other components. If the user performs an action that starts another activity or switches to another app, the system calls another set of lifecycle methods on your activity as it moves into the background (where the activity is no longer visible, but the instance and its state remains intact).

Within the lifecycle callback methods, you can declare how your activity behaves when the user leaves and re-enters the activity. For example, if you're building a streaming video player, you might pause the video and terminate the network connection when the user switches to another app. When the user returns, you can reconnect to the network and allow the user to resume the video from the same spot.

This class explains important lifecycle callback methods that each Activity instance receives and how you can use them so your activity does what the user expects and does not consume system resources when your activity doesn't need them.

Lessons

Starting an Activity
Learn the basics about the activity lifecycle, how the user can launch your app, and how to perform basic activity creation.
Pausing and Resuming an Activity
Learn what happens when your activity is paused (partially obscured) and resumed and what you should do during these state changes.
Stopping and Restarting an Activity
Learn what happens when the user completely leaves your activity and returns to it.
Recreating an Activity
Learn what happens when your activity is destroyed and how you can rebuild the activity state when necessary.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Starting an Activity | Android Developers Skip to content

Most visited

Recently visited

navigation

Starting an Activity

Unlike other programming paradigms in which apps are launched with a main() method, the Android system initiates code in an Activity instance by invoking specific callback methods that correspond to specific stages of its lifecycle. There is a sequence of callback methods that start up an activity and a sequence of callback methods that tear down an activity.

This lesson provides an overview of the most important lifecycle methods and shows you how to handle the first lifecycle callback that creates a new instance of your activity.

Understand the Lifecycle Callbacks

During the life of an activity, the system calls a core set of lifecycle methods in a sequence similar to a step pyramid. That is, each stage of the activity lifecycle is a separate step on the pyramid. As the system creates a new activity instance, each callback method moves the activity state one step toward the top. The top of the pyramid is the point at which the activity is running in the foreground and the user can interact with it.

As the user begins to leave the activity, the system calls other methods that move the activity state back down the pyramid in order to dismantle the activity. In some cases, the activity will move only part way down the pyramid and wait (such as when the user switches to another app), from which point the activity can move back to the top (if the user returns to the activity) and resume where the user left off.

Figure 1. A simplified illustration of the Activity lifecycle, expressed as a step pyramid. This shows how, for every callback used to take the activity a step toward the Resumed state at the top, there's a callback method that takes the activity a step down. The activity can also return to the resumed state from the Paused and Stopped state.

Depending on the complexity of your activity, you probably don't need to implement all the lifecycle methods. However, it's important that you understand each one and implement those that ensure your app behaves the way users expect. Implementing your activity lifecycle methods properly ensures your app behaves well in several ways, including that it:

  • Does not crash if the user receives a phone call or switches to another app while using your app.
  • Does not consume valuable system resources when the user is not actively using it.
  • Does not lose the user's progress if they leave your app and return to it at a later time.
  • Does not crash or lose the user's progress when the screen rotates between landscape and portrait orientation.

As you'll learn in the following lessons, there are several situations in which an activity transitions between different states that are illustrated in figure 1. However, only three of these states can be static. That is, the activity can exist in one of only three states for an extended period of time:

Resumed
In this state, the activity is in the foreground and the user can interact with it. (Also sometimes referred to as the "running" state.)
Paused
In this state, the activity is partially obscured by another activity—the other activity that's in the foreground is semi-transparent or doesn't cover the entire screen. The paused activity does not receive user input and cannot execute any code.
Stopped
In this state, the activity is completely hidden and not visible to the user; it is considered to be in the background. While stopped, the activity instance and all its state information such as member variables is retained, but it cannot execute any code.

The other states (Created and Started) are transient and the system quickly moves from them to the next state by calling the next lifecycle callback method. That is, after the system calls onCreate(), it quickly calls onStart(), which is quickly followed by onResume().

That's it for the basic activity lifecycle. Now you'll start learning about some of the specific lifecycle behaviors.

Specify Your App's Launcher Activity

When the user selects your app icon from the Home screen, the system calls the onCreate() method for the Activity in your app that you've declared to be the "launcher" (or "main") activity. This is the activity that serves as the main entry point to your app's user interface.

You can define which activity to use as the main activity in the Android manifest file, AndroidManifest.xml, which is at the root of your project directory.

The main activity for your app must be declared in the manifest with an <intent-filter> that includes the MAIN action and LAUNCHER category. For example:

<activity android:name=".MainActivity" android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Note: When you create a new Android project with the Android SDK tools, the default project files include an Activity class that's declared in the manifest with this filter.

If either the MAIN action or LAUNCHER category are not declared for one of your activities, then your app icon will not appear in the Home screen's list of apps.

Create a New Instance

Most apps include several different activities that allow the user to perform different actions. Whether an activity is the main activity that's created when the user clicks your app icon or a different activity that your app starts in response to a user action, the system creates every new instance of Activity by calling its onCreate() method.

You must implement the onCreate() method to perform basic application startup logic that should happen only once for the entire life of the activity. For example, your implementation of onCreate() should define the user interface and possibly instantiate some class-scope variables.

For example, the following example of the onCreate() method shows some code that performs some fundamental setup for the activity, such as declaring the user interface (defined in an XML layout file), defining member variables, and configuring some of the UI.

TextView mTextView; // Member variable for text view in the layout

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Set the user interface layout for this Activity
    // The layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);
    
    // Initialize member TextView so we can manipulate it later
    mTextView = (TextView) findViewById(R.id.text_message);
    
    // Make sure we're running on Honeycomb or higher to use ActionBar APIs
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        // For the main activity, make sure the app icon in the action bar
        // does not behave as a button
        ActionBar actionBar = getActionBar();
        actionBar.setHomeButtonEnabled(false);
    }
}

Caution: Using the SDK_INT to prevent older systems from executing new APIs works in this way on Android 2.0 (API level 5) and higher only. Older versions will encounter a runtime exception.

Once the onCreate() finishes execution, the system calls the onStart() and onResume() methods in quick succession. Your activity never resides in the Created or Started states. Technically, the activity becomes visible to the user when onStart() is called, but onResume() quickly follows and the activity remains in the Resumed state until something occurs to change that, such as when a phone call is received, the user navigates to another activity, or the device screen turns off.

In the other lessons that follow, you'll see how the other start up methods, onStart() and onResume(), are useful during your activity's lifecycle when used to resume the activity from the Paused or Stopped states.

Note: The onCreate() method includes a parameter called savedInstanceState that's discussed in the latter lesson about Recreating an Activity.

Figure 2. Another illustration of the activity lifecycle structure with an emphasis on the three main callbacks that the system calls in sequence when creating a new instance of the activity: onCreate(), onStart(), and onResume(). Once this sequence of callbacks complete, the activity reaches the Resumed state where users can interact with the activity until they switch to a different activity.

Destroy the Activity

While the activity's first lifecycle callback is onCreate(), its very last callback is onDestroy(). The system calls this method on your activity as the final signal that your activity instance is being completely removed from the system memory.

Most apps don't need to implement this method because local class references are destroyed with the activity and your activity should perform most cleanup during onPause() and onStop(). However, if your activity includes background threads that you created during onCreate() or other long-running resources that could potentially leak memory if not properly closed, you should kill them during onDestroy().

@Override
public void onDestroy() {
    super.onDestroy();  // Always call the superclass
    
    // Stop method tracing that the activity started during onCreate()
    android.os.Debug.stopMethodTracing();
}

Note: The system calls onDestroy() after it has already called onPause() and onStop() in all situations except one: when you call finish() from within the onCreate() method. In some cases, such as when your activity operates as a temporary decision maker to launch another activity, you might call finish() from within onCreate() to destroy the activity. In this case, the system immediately calls onDestroy() without calling any of the other lifecycle methods.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Pausing and Resuming an Activity | Android Developers Skip to content

Most visited

Recently visited

navigation

Pausing and Resuming an Activity

This lesson teaches you to

  1. Pause Your Activity
  2. Resume Your Activity

You should also read

Try it out

Download the demo

ActivityLifecycle.zip

During normal app use, the foreground activity is sometimes obstructed by other visual components that cause the activity to pause. For example, when a semi-transparent activity opens (such as one in the style of a dialog), the previous activity pauses. As long as the activity is still partially visible but currently not the activity in focus, it remains paused.

However, once the activity is fully-obstructed and not visible, it stops (which is discussed in the next lesson).

As your activity enters the paused state, the system calls the onPause() method on your Activity, which allows you to stop ongoing actions that should not continue while paused (such as a video) or persist any information that should be permanently saved in case the user continues to leave your app. If the user returns to your activity from the paused state, the system resumes it and calls the onResume() method.

Note: When your activity receives a call to onPause(), it may be an indication that the activity will be paused for a moment and the user may return focus to your activity. However, it's usually the first indication that the user is leaving your activity.

Figure 1. When a semi-transparent activity obscures your activity, the system calls onPause() and the activity waits in the Paused state (1). If the user returns to the activity while it's still paused, the system calls onResume() (2).

Pause Your Activity

When the system calls onPause() for your activity, it technically means your activity is still partially visible, but most often is an indication that the user is leaving the activity and it will soon enter the Stopped state. You should usually use the onPause() callback to:

  • Stop animations or other ongoing actions that could consume CPU.
  • Commit unsaved changes, but only if users expect such changes to be permanently saved when they leave (such as a draft email).
  • Release system resources, such as broadcast receivers, handles to sensors (like GPS), or any resources that may affect battery life while your activity is paused and the user does not need them.

For example, if your application uses the Camera, the onPause() method is a good place to release it.

@Override
public void onPause() {
    super.onPause();  // Always call the superclass method first

    // Release the Camera because we don't need it when paused
    // and other activities might need to use it.
    if (mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}

Generally, you should not use onPause() to store user changes (such as personal information entered into a form) to permanent storage. The only time you should persist user changes to permanent storage within onPause() is when you're certain users expect the changes to be auto-saved (such as when drafting an email). However, you should avoid performing CPU-intensive work during onPause(), such as writing to a database, because it can slow the visible transition to the next activity (you should instead perform heavy-load shutdown operations during onStop()).

You should keep the amount of operations done in the onPause() method relatively simple in order to allow for a speedy transition to the user's next destination if your activity is actually being stopped.

Note: When your activity is paused, the Activity instance is kept resident in memory and is recalled when the activity resumes. You don’t need to re-initialize components that were created during any of the callback methods leading up to the Resumed state.

Resume Your Activity

When the user resumes your activity from the Paused state, the system calls the onResume() method.

Be aware that the system calls this method every time your activity comes into the foreground, including when it's created for the first time. As such, you should implement onResume() to initialize components that you release during onPause() and perform any other initializations that must occur each time the activity enters the Resumed state (such as begin animations and initialize components only used while the activity has user focus).

The following example of onResume() is the counterpart to the onPause() example above, so it initializes the camera that's released when the activity pauses.

@Override
public void onResume() {
    super.onResume();  // Always call the superclass method first

    // Get the Camera instance as the activity achieves full user focus
    if (mCamera == null) {
        initializeCamera(); // Local method to handle camera init
    }
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Stopping and Restarting an Activity | Android Developers Skip to content

Most visited

Recently visited

navigation

Stopping and Restarting an Activity

This lesson teaches you to

  1. Stop Your Activity
  2. Start/Restart Your Activity

You should also read

Try it out

Download the demo

ActivityLifecycle.zip

Properly stopping and restarting your activity is an important process in the activity lifecycle that ensures your users perceive that your app is always alive and doesn't lose their progress. There are a few of key scenarios in which your activity is stopped and restarted:

  • The user opens the Recent Apps window and switches from your app to another app. The activity in your app that's currently in the foreground is stopped. If the user returns to your app from the Home screen launcher icon or the Recent Apps window, the activity restarts.
  • The user performs an action in your app that starts a new activity. The current activity is stopped when the second activity is created. If the user then presses the Back button, the first activity is restarted.
  • The user receives a phone call while using your app on his or her phone.

The Activity class provides two lifecycle methods, onStop() and onRestart(), which allow you to specifically handle how your activity handles being stopped and restarted. Unlike the paused state, which identifies a partial UI obstruction, the stopped state guarantees that the UI is no longer visible and the user's focus is in a separate activity (or an entirely separate app).

Note: Because the system retains your Activity instance in system memory when it is stopped, it's possible that you don't need to implement the onStop() and onRestart() (or even onStart() methods at all. For most activities that are relatively simple, the activity will stop and restart just fine and you might only need to use onPause() to pause ongoing actions and disconnect from system resources.

Figure 1. When the user leaves your activity, the system calls onStop() to stop the activity (1). If the user returns while the activity is stopped, the system calls onRestart() (2), quickly followed by onStart() (3) and onResume() (4). Notice that no matter what scenario causes the activity to stop, the system always calls onPause() before calling onStop().

Stop Your Activity

When your activity receives a call to the onStop() method, it's no longer visible and should release almost all resources that aren't needed while the user is not using it. Once your activity is stopped, the system might destroy the instance if it needs to recover system memory. In extreme cases, the system might simply kill your app process without calling the activity's final onDestroy() callback, so it's important you use onStop() to release resources that might leak memory.

Although the onPause() method is called before onStop(), you should use onStop() to perform larger, more CPU intensive shut-down operations, such as writing information to a database.

For example, here's an implementation of onStop() that saves the contents of a draft note to persistent storage:

@Override
protected void onStop() {
    super.onStop();  // Always call the superclass method first

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    getContentResolver().update(
            mUri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
            );
}

When your activity is stopped, the Activity object is kept resident in memory and is recalled when the activity resumes. You don’t need to re-initialize components that were created during any of the callback methods leading up to the Resumed state. The system also keeps track of the current state for each View in the layout, so if the user entered text into an EditText widget, that content is retained so you don't need to save and restore it.

Note: Even if the system destroys your activity while it's stopped, it still retains the state of the View objects (such as text in an EditText) in a Bundle (a blob of key-value pairs) and restores them if the user navigates back to the same instance of the activity (the next lesson talks more about using a Bundle to save other state data in case your activity is destroyed and recreated).

Start/Restart Your Activity

When your activity comes back to the foreground from the stopped state, it receives a call to onRestart(). The system also calls the onStart() method, which happens every time your activity becomes visible (whether being restarted or created for the first time). The onRestart() method, however, is called only when the activity resumes from the stopped state, so you can use it to perform special restoration work that might be necessary only if the activity was previously stopped, but not destroyed.

It's uncommon that an app needs to use onRestart() to restore the activity's state, so there aren't any guidelines for this method that apply to the general population of apps. However, because your onStop() method should essentially clean up all your activity's resources, you'll need to re-instantiate them when the activity restarts. Yet, you also need to instantiate them when your activity is created for the first time (when there's no existing instance of the activity). For this reason, you should usually use the onStart() callback method as the counterpart to the onStop() method, because the system calls onStart() both when it creates your activity and when it restarts the activity from the stopped state.

For example, because the user might have been away from your app for a long time before coming back it, the onStart() method is a good place to verify that required system features are enabled:

@Override
protected void onStart() {
    super.onStart();  // Always call the superclass method first
    
    // The activity is either being restarted or started for the first time
    // so this is where we should make sure that GPS is enabled
    LocationManager locationManager = 
            (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    
    if (!gpsEnabled) {
        // Create a dialog here that requests the user to enable GPS, and use an intent
        // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action
        // to take the user to the Settings screen to enable GPS when they click "OK"
    }
}

@Override
protected void onRestart() {
    super.onRestart();  // Always call the superclass method first
    
    // Activity being restarted from stopped state    
}

When the system destroys your activity, it calls the onDestroy() method for your Activity. Because you should generally have released most of your resources with onStop(), by the time you receive a call to onDestroy(), there's not much that most apps need to do. This method is your last chance to clean out resources that could lead to a memory leak, so you should be sure that additional threads are destroyed and other long-running actions like method tracing are also stopped.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Recreating an Activity | Android Developers Skip to content

Most visited

Recently visited

navigation

Recreating an Activity

There are a few scenarios in which your activity is destroyed due to normal app behavior, such as when the user presses the Back button or your activity signals its own destruction by calling finish(). The system may also destroy your activity if it's currently stopped and hasn't been used in a long time or the foreground activity requires more resources so the system must shut down background processes to recover memory.

When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed. However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actual Activity instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the "instance state" and is a collection of key-value pairs stored in a Bundle object.

Caution: Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout).

By default, the system uses the Bundle instance state to save information about each View object in your activity layout (such as the text value entered into an EditText object). So, if your activity instance is destroyed and recreated, the state of the layout is restored to its previous state with no code required by you. However, your activity might have more state information that you'd like to restore, such as member variables that track the user's progress in the activity.

Note: In order for the Android system to restore the state of the views in your activity, each view must have a unique ID, supplied by the android:id attribute.

To save additional data about the activity state, you must override the onSaveInstanceState() callback method. The system calls this method when the user is leaving your activity and passes it the Bundle object that will be saved in the event that your activity is destroyed unexpectedly. If the system must recreate the activity instance later, it passes the same Bundle object to both the onRestoreInstanceState() and onCreate() methods.

Figure 2. As the system begins to stop your activity, it calls onSaveInstanceState() (1) so you can specify additional state data you'd like to save in case the Activity instance must be recreated. If the activity is destroyed and the same instance must be recreated, the system passes the state data defined at (1) to both the onCreate() method (2) and the onRestoreInstanceState() method (3).

Save Your Activity State

As your activity begins to stop, the system calls onSaveInstanceState() so your activity can save state information with a collection of key-value pairs. The default implementation of this method saves information about the state of the activity's view hierarchy, such as the text in an EditText widget or the scroll position of a ListView.

To save additional state information for your activity, you must implement onSaveInstanceState() and add key-value pairs to the Bundle object. For example:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
    
    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

Caution: Always call the superclass implementation of onSaveInstanceState() so the default implementation can save the state of the view hierarchy.

Restore Your Activity State

When your activity is recreated after it was previously destroyed, you can recover your saved state from the Bundle that the system passes your activity. Both the onCreate() and onRestoreInstanceState() callback methods receive the same Bundle that contains the instance state information.

Because the onCreate() method is called whether the system is creating a new instance of your activity or recreating a previous one, you must check whether the state Bundle is null before you attempt to read it. If it is null, then the system is creating a new instance of the activity, instead of restoring a previous one that was destroyed.

For example, here's how you can restore some state data in onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first
   
    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    ...
}

Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null:

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);
   
    // Restore state members from saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

Caution: Always call the superclass implementation of onRestoreInstanceState() so the default implementation can restore the state of the view hierarchy.

To learn more about recreating your activity due to a restart event at runtime (such as when the screen rotates), read Handling Runtime Changes.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Building a Dynamic UI with Fragments | Android Developers Skip to content

Most visited

Recently visited

navigation

Building a Dynamic UI with Fragments

Dependencies and prerequisites

You should also read

Try it out

Download the sample

FragmentBasics.zip

To create a dynamic and multi-pane user interface on Android, you need to encapsulate UI components and activity behaviors into modules that you can swap into and out of your activities. You can create these modules with the Fragment class, which behaves somewhat like a nested activity that can define its own layout and manage its own lifecycle.

When a fragment specifies its own layout, it can be configured in different combinations with other fragments inside an activity to modify your layout configuration for different screen sizes (a small screen might show one fragment at a time, but a large screen can show two or more).

This class shows you how to create a dynamic user experience with fragments and optimize your app's user experience for devices with different screen sizes, all while continuing to support devices running versions as old as Android 1.6.

Lessons

Creating a Fragment
Learn how to build a fragment and implement basic behaviors within its callback methods.
Building a Flexible UI
Learn how to build your app with layouts that provide different fragment configurations for different screens.
Communicating with Other Fragments
Learn how to set up communication paths from a fragment to the activity and other fragments.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Creating a Fragment | Android Developers Skip to content

Most visited

Recently visited

navigation

Creating a Fragment

This lesson teaches you to

  1. Create a Fragment Class
  2. Add a Fragment to an Activity using XML

You should also read

Try it out

Download the sample

FragmentBasics.zip

You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a "sub activity" that you can reuse in different activities). This lesson shows how to extend the Fragment class using the Support Library so your app remains compatible with devices running system versions as low as Android 1.6.

Before you begin this lesson, you must set up your Android project to use the Support Library. If you have not used the Support Library before, set up your project to use the v4 library by following the Support Library Setup document. However, you can also include the app bar in your activities by instead using the v7 appcompat library, which is compatible with Android 2.1 (API level 7) and also includes the Fragment APIs.

Create a Fragment Class

To create a fragment, extend the Fragment class, then override key lifecycle methods to insert your app logic, similar to the way you would with an Activity class.

One difference when creating a Fragment is that you must use the onCreateView() callback to define the layout. In fact, this is the only callback you need in order to get a fragment running. For example, here's a simple fragment that specifies its own layout:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.ViewGroup;

public class ArticleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.article_view, container, false);
    }
}

Just like an activity, a fragment should implement other lifecycle callbacks that allow you to manage its state as it is added or removed from the activity and as the activity transitions between its lifecycle states. For instance, when the activity's onPause() method is called, any fragments in the activity also receive a call to onPause().

More information about the fragment lifecycle and callback methods is available in the Fragments developer guide.

Add a Fragment to an Activity using XML

While fragments are reusable, modular UI components, each instance of a Fragment class must be associated with a parent FragmentActivity. You can achieve this association by defining each fragment within your activity layout XML file.

Note: FragmentActivity is a special activity provided in the Support Library to handle fragments on system versions older than API level 11. If the lowest system version you support is API level 11 or higher, then you can use a regular Activity.

Here is an example layout file that adds two fragments to an activity when the device screen is considered "large" (specified by the large qualifier in the directory name).

res/layout-large/news_articles.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>

Tip: For more about creating layouts for different screen sizes, read Supporting Different Screen Sizes.

Then apply the layout to your activity:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);
    }
}

If you're using the v7 appcompat library, your activity should instead extend AppCompatActivity, which is a subclass of FragmentActivity. For more information, read Adding the App Bar).

Note: When you add a fragment to an activity layout by defining the fragment in the layout XML file, you cannot remove the fragment at runtime. If you plan to swap your fragments in and out during user interaction, you must add the fragment to the activity when the activity first starts, as shown in the next lesson.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Building a Flexible UI | Android Developers Skip to content

Most visited

Recently visited

navigation

Building a Flexible UI

When designing your application to support a wide range of screen sizes, you can reuse your fragments in different layout configurations to optimize the user experience based on the available screen space.

For example, on a handset device it might be appropriate to display just one fragment at a time for a single-pane user interface. Conversely, you may want to set fragments side-by-side on a tablet which has a wider screen size to display more information to the user.

Figure 1. Two fragments, displayed in different configurations for the same activity on different screen sizes. On a large screen, both fragments fit side by side, but on a handset device, only one fragment fits at a time so the fragments must replace each other as the user navigates.

The FragmentManager class provides methods that allow you to add, remove, and replace fragments to an activity at runtime in order to create a dynamic experience.

Add a Fragment to an Activity at Runtime

Rather than defining the fragments for an activity in the layout file—as shown in the previous lesson with the <fragment> element—you can add a fragment to the activity during the activity runtime. This is necessary if you plan to change fragments during the life of the activity.

To perform a transaction such as add or remove a fragment, you must use the FragmentManager to create a FragmentTransaction, which provides APIs to add, remove, replace, and perform other fragment transactions.

If your activity allows the fragments to be removed and replaced, you should add the initial fragment(s) to the activity during the activity's onCreate() method.

An important rule when dealing with fragments—especially when adding fragments at runtime—is that your activity layout must include a container View in which you can insert the fragment.

The following layout is an alternative to the layout shown in the previous lesson that shows only one fragment at a time. In order to replace one fragment with another, the activity's layout includes an empty FrameLayout that acts as the fragment container.

Notice that the filename is the same as the layout file in the previous lesson, but the layout directory does not have the large qualifier, so this layout is used when the device screen is smaller than large because the screen does not fit both fragments at the same time.

res/layout/news_articles.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Inside your activity, call getSupportFragmentManager() to get a FragmentManager using the Support Library APIs. Then call beginTransaction() to create a FragmentTransaction and call add() to add a fragment.

You can perform multiple fragment transaction for the activity using the same FragmentTransaction. When you're ready to make the changes, you must call commit().

For example, here's how to add a fragment to the previous layout:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);

        // Check that the activity is using the layout version with
        // the fragment_container FrameLayout
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            if (savedInstanceState != null) {
                return;
            }

            // Create a new Fragment to be placed in the activity layout
            HeadlinesFragment firstFragment = new HeadlinesFragment();
            
            // In case this activity was started with special instructions from an
            // Intent, pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());
            
            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }
}

Because the fragment has been added to the FrameLayout container at runtime—instead of defining it in the activity's layout with a <fragment> element—the activity can remove the fragment and replace it with a different one.

Replace One Fragment with Another

The procedure to replace a fragment is similar to adding one, but requires the replace() method instead of add().

Keep in mind that when you perform fragment transactions, such as replace or remove one, it's often appropriate to allow the user to navigate backward and "undo" the change. To allow the user to navigate backward through the fragment transactions, you must call addToBackStack() before you commit the FragmentTransaction.

Note: When you remove or replace a fragment and add the transaction to the back stack, the fragment that is removed is stopped (not destroyed). If the user navigates back to restore the fragment, it restarts. If you do not add the transaction to the back stack, then the fragment is destroyed when removed or replaced.

Example of replacing one fragment with another:

// Create fragment and give it an argument specifying the article it should show
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

The addToBackStack() method takes an optional string parameter that specifies a unique name for the transaction. The name isn't needed unless you plan to perform advanced fragment operations using the FragmentManager.BackStackEntry APIs.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Communicating with Other Fragments | Android Developers Skip to content

Most visited

Recently visited

navigation

Communicating with Other Fragments

This lesson teaches you to

  1. Define an Interface
  2. Implement the Interface
  3. Deliver a Message to a Fragment

You should also read

Try it out

Download the sample

FragmentBasics.zip

In order to reuse the Fragment UI components, you should build each as a completely self-contained, modular component that defines its own layout and behavior. Once you have defined these reusable Fragments, you can associate them with an Activity and connect them with the application logic to realize the overall composite UI.

Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.

Define an Interface

To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.

Here is an example of Fragment to Activity communication:

public class HeadlinesFragment extends ListFragment {
    OnHeadlineSelectedListener mCallback;

    // Container Activity must implement this interface
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        
        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mCallback = (OnHeadlineSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnHeadlineSelectedListener");
        }
    }
    
    ...
}

Now the fragment can deliver messages to the activity by calling the onArticleSelected() method (or other methods in the interface) using the mCallback instance of the OnHeadlineSelectedListener interface.

For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Send the event to the host activity
        mCallback.onArticleSelected(position);
    }

Implement the Interface

In order to receive event callbacks from the fragment, the activity that hosts it must implement the interface defined in the fragment class.

For example, the following activity implements the interface from the above example.

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...
    
    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

Deliver a Message to a Fragment

The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.

For instance, imagine that the activity shown above may contain another fragment that's used to display the item specified by the data returned in the above callback method. In this case, the activity can pass the information received in the callback method to the other fragment that will display the item:

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.article_fragment);

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position);
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...

            // Create fragment and give it an argument for the selected article
            ArticleFragment newFragment = new ArticleFragment();
            Bundle args = new Bundle();
            args.putInt(ArticleFragment.ARG_POSITION, position);
            newFragment.setArguments(args);
        
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

            // Replace whatever is in the fragment_container view with this fragment,
            // and add the transaction to the back stack so the user can navigate back
            transaction.replace(R.id.fragment_container, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();
        }
    }
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Saving Data | Android Developers Skip to content

Most visited

Recently visited

navigation

Saving Data

Dependencies and prerequisites

  • Android 1.6 (API Level 4) or higher
  • Familiarity with Map key-value collections
  • Familiarity with the Java file I/O API
  • Familiarity with SQL databases

You should also read

Most Android apps need to save data, even if only to save information about the app state during onPause() so the user's progress is not lost. Most non-trivial apps also need to save user settings, and some apps must manage large amounts of information in files and databases. This class introduces you to the principal data storage options in Android, including:

  • Saving key-value pairs of simple data types in a shared preferences file
  • Saving arbitrary files in Android's file system
  • Using databases managed by SQLite

Lessons

Saving Key-Value Sets
Learn to use a shared preferences file for storing small amounts of information in key-value pairs.
Saving Files
Learn to save a basic file, such as to store long sequences of data that are generally read in order.
Saving Data in SQL Databases
Learn to use a SQLite database to read and write structured data.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Saving Key-Value Sets | Android Developers Skip to content

Most visited

Recently visited

navigation

Saving Key-Value Sets

If you have a relatively small collection of key-values that you'd like to save, you should use the SharedPreferences APIs. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. Each SharedPreferences file is managed by the framework and can be private or shared.

This class shows you how to use the SharedPreferences APIs to store and retrieve simple values.

Note: The SharedPreferences APIs are only for reading and writing key-value pairs and you should not confuse them with the Preference APIs, which help you build a user interface for your app settings (although they use SharedPreferences as their implementation to save the app settings). For information about using the Preference APIs, see the Settings guide.

Get a Handle to a SharedPreferences

You can create a new shared preference file or access an existing one by calling one of two methods:

  • getSharedPreferences() — Use this if you need multiple shared preference files identified by name, which you specify with the first parameter. You can call this from any Context in your app.
  • getPreferences() — Use this from an Activity if you need to use only one shared preference file for the activity. Because this retrieves a default shared preference file that belongs to the activity, you don't need to supply a name.

For example, the following code is executed inside a Fragment. It accesses the shared preferences file that's identified by the resource string R.string.preference_file_key and opens it using the private mode so the file is accessible by only your app.

Context context = getActivity();
SharedPreferences sharedPref = context.getSharedPreferences(
        getString(R.string.preference_file_key), Context.MODE_PRIVATE);

When naming your shared preference files, you should use a name that's uniquely identifiable to your app, such as "com.example.myapp.PREFERENCE_FILE_KEY"

Alternatively, if you need just one shared preference file for your activity, you can use the getPreferences() method:

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

Caution: If you create a shared preferences file with MODE_WORLD_READABLE or MODE_WORLD_WRITEABLE, then any other apps that know the file identifier can access your data.

Write to Shared Preferences

To write to a shared preferences file, create a SharedPreferences.Editor by calling edit() on your SharedPreferences.

Pass the keys and values you want to write with methods such as putInt() and putString(). Then call commit() to save the changes. For example:

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(getString(R.string.saved_high_score), newHighScore);
editor.commit();

Read from Shared Preferences

To retrieve values from a shared preferences file, call methods such as getInt() and getString(), providing the key for the value you want, and optionally a default value to return if the key isn't present. For example:

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
int defaultValue = getResources().getInteger(R.string.saved_high_score_default);
long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Saving Files | Android Developers Skip to content

Most visited

Recently visited

navigation

Saving Files

Android uses a file system that's similar to disk-based file systems on other platforms. This lesson describes how to work with the Android file system to read and write files with the File APIs.

A File object is suited to reading or writing large amounts of data in start-to-finish order without skipping around. For example, it's good for image files or anything exchanged over a network.

This lesson shows how to perform basic file-related tasks in your app. The lesson assumes that you are familiar with the basics of the Linux file system and the standard file input/output APIs in java.io.

Choose Internal or External Storage

All Android devices have two file storage areas: "internal" and "external" storage. These names come from the early days of Android, when most devices offered built-in non-volatile memory (internal storage), plus a removable storage medium such as a micro SD card (external storage). Some devices divide the permanent storage space into "internal" and "external" partitions, so even without a removable storage medium, there are always two storage spaces and the API behavior is the same whether the external storage is removable or not. The following lists summarize the facts about each storage space.

Internal storage:

  • It's always available.
  • Files saved here are accessible by only your app.
  • When the user uninstalls your app, the system removes all your app's files from internal storage.

Internal storage is best when you want to be sure that neither the user nor other apps can access your files.

External storage:

  • It's not always available, because the user can mount the external storage as USB storage and in some cases remove it from the device.
  • It's world-readable, so files saved here may be read outside of your control.
  • When the user uninstalls your app, the system removes your app's files from here only if you save them in the directory from getExternalFilesDir().

External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.

Note: Before Android N, internal files could be made accessible to other apps by means of relaxing file system permissions. This is no longer the case. If you wish to make the content of a private file accessible to other apps, your app may use the FileProvider. See Sharing Files.

Tip: Although apps are installed onto the internal storage by default, you can specify the android:installLocation attribute in your manifest so your app may be installed on external storage. Users appreciate this option when the APK size is very large and they have an external storage space that's larger than the internal storage. For more information, see App Install Location.

Obtain Permissions for External Storage

To write to the external storage, you must request the WRITE_EXTERNAL_STORAGE permission in your manifest file:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

Caution: Currently, all apps have the ability to read the external storage without a special permission. However, this will change in a future release. If your app needs to read the external storage (but not write to it), then you will need to declare the READ_EXTERNAL_STORAGE permission. To ensure that your app continues to work as expected, you should declare this permission now, before the change takes effect.

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>

However, if your app uses the WRITE_EXTERNAL_STORAGE permission, then it implicitly has permission to read the external storage as well.

You don’t need any permissions to save files on the internal storage. Your application always has permission to read and write files in its internal storage directory.

Save a File on Internal Storage

When saving a file to internal storage, you can acquire the appropriate directory as a File by calling one of two methods:

getFilesDir()
Returns a File representing an internal directory for your app.
getCacheDir()
Returns a File representing an internal directory for your app's temporary cache files. Be sure to delete each file once it is no longer needed and implement a reasonable size limit for the amount of memory you use at any given time, such as 1MB. If the system begins running low on storage, it may delete your cache files without warning.

To create a new file in one of these directories, you can use the File() constructor, passing the File provided by one of the above methods that specifies your internal storage directory. For example:

File file = new File(context.getFilesDir(), filename);

Alternatively, you can call openFileOutput() to get a FileOutputStream that writes to a file in your internal directory. For example, here's how to write some text to a file:

String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;

try {
  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
  outputStream.write(string.getBytes());
  outputStream.close();
} catch (Exception e) {
  e.printStackTrace();
}

Or, if you need to cache some files, you should instead use createTempFile(). For example, the following method extracts the file name from a URL and creates a file with that name in your app's internal cache directory:

public File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    catch (IOException e) {
        // Error while creating file
    }
    return file;
}

Note: Your app's internal storage directory is specified by your app's package name in a special location of the Android file system. Technically, another app can read your internal files if you set the file mode to be readable. However, the other app would also need to know your app package name and file names. Other apps cannot browse your internal directories and do not have read or write access unless you explicitly set the files to be readable or writable. So as long as you use MODE_PRIVATE for your files on the internal storage, they are never accessible to other apps.

Save a File on External Storage

Because the external storage may be unavailable—such as when the user has mounted the storage to a PC or has removed the SD card that provides the external storage—you should always verify that the volume is available before accessing it. You can query the external storage state by calling getExternalStorageState(). If the returned state is equal to MEDIA_MOUNTED, then you can read and write your files. For example, the following methods are useful to determine the storage availability:

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

Although the external storage is modifiable by the user and other apps, there are two categories of files you might save here:

Public files
Files that should be freely available to other apps and to the user. When the user uninstalls your app, these files should remain available to the user.

For example, photos captured by your app or other downloaded files.

Private files
Files that rightfully belong to your app and should be deleted when the user uninstalls your app. Although these files are technically accessible by the user and other apps because they are on the external storage, they are files that realistically don't provide value to the user outside your app. When the user uninstalls your app, the system deletes all files in your app's external private directory.

For example, additional resources downloaded by your app or temporary media files.

If you want to save public files on the external storage, use the getExternalStoragePublicDirectory() method to get a File representing the appropriate directory on the external storage. The method takes an argument specifying the type of file you want to save so that they can be logically organized with other public files, such as DIRECTORY_MUSIC or DIRECTORY_PICTURES. For example:

public File getAlbumStorageDir(String albumName) {
    // Get the directory for the user's public pictures directory. 
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

If you want to save files that are private to your app, you can acquire the appropriate directory by calling getExternalFilesDir() and passing it a name indicating the type of directory you'd like. Each directory created this way is added to a parent directory that encapsulates all your app's external storage files, which the system deletes when the user uninstalls your app.

For example, here's a method you can use to create a directory for an individual photo album:

public File getAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory. 
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

If none of the pre-defined sub-directory names suit your files, you can instead call getExternalFilesDir() and pass null. This returns the root directory for your app's private directory on the external storage.

Remember that getExternalFilesDir() creates a directory inside a directory that is deleted when the user uninstalls your app. If the files you're saving should remain available after the user uninstalls your app—such as when your app is a camera and the user will want to keep the photos—you should instead use getExternalStoragePublicDirectory().

Regardless of whether you use getExternalStoragePublicDirectory() for files that are shared or getExternalFilesDir() for files that are private to your app, it's important that you use directory names provided by API constants like DIRECTORY_PICTURES. These directory names ensure that the files are treated properly by the system. For instance, files saved in DIRECTORY_RINGTONES are categorized by the system media scanner as ringtones instead of music.

Query Free Space

If you know ahead of time how much data you're saving, you can find out whether sufficient space is available without causing an IOException by calling getFreeSpace() or getTotalSpace(). These methods provide the current available space and the total space in the storage volume, respectively. This information is also useful to avoid filling the storage volume above a certain threshold.

However, the system does not guarantee that you can write as many bytes as are indicated by getFreeSpace(). If the number returned is a few MB more than the size of the data you want to save, or if the file system is less than 90% full, then it's probably safe to proceed. Otherwise, you probably shouldn't write to storage.

Note: You aren't required to check the amount of available space before you save your file. You can instead try writing the file right away, then catch an IOException if one occurs. You may need to do this if you don't know exactly how much space you need. For example, if you change the file's encoding before you save it by converting a PNG image to JPEG, you won't know the file's size beforehand.

Delete a File

You should always delete files that you no longer need. The most straightforward way to delete a file is to have the opened file reference call delete() on itself.

myFile.delete();

If the file is saved on internal storage, you can also ask the Context to locate and delete a file by calling deleteFile():

myContext.deleteFile(fileName);

Note: When the user uninstalls your app, the Android system deletes the following:

  • All files you saved on internal storage
  • All files you saved on external storage using getExternalFilesDir().

However, you should manually delete all cached files created with getCacheDir() on a regular basis and also regularly delete other files you no longer need.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Saving Data in SQL Databases | Android Developers Skip to content

Most visited

Recently visited

navigation

Saving Data in SQL Databases

Saving data to a database is ideal for repeating or structured data, such as contact information. This class assumes that you are familiar with SQL databases in general and helps you get started with SQLite databases on Android. The APIs you'll need to use a database on Android are available in the android.database.sqlite package.

Define a Schema and Contract

One of the main principles of SQL databases is the schema: a formal declaration of how the database is organized. The schema is reflected in the SQL statements that you use to create your database. You may find it helpful to create a companion class, known as a contract class, which explicitly specifies the layout of your schema in a systematic and self-documenting way.

A contract class is a container for constants that define names for URIs, tables, and columns. The contract class allows you to use the same constants across all the other classes in the same package. This lets you change a column name in one place and have it propagate throughout your code.

A good way to organize a contract class is to put definitions that are global to your whole database in the root level of the class. Then create an inner class for each table that enumerates its columns.

Note: By implementing the BaseColumns interface, your inner class can inherit a primary key field called _ID that some Android classes such as cursor adaptors will expect it to have. It's not required, but this can help your database work harmoniously with the Android framework.

For example, this snippet defines the table name and column names for a single table:

public final class FeedReaderContract {
    // To prevent someone from accidentally instantiating the contract class,
    // give it an empty constructor.
    public FeedReaderContract() {}

    /* Inner class that defines the table contents */
    public static abstract class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_ENTRY_ID = "entryid";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
        ...
    }
}

Create a Database Using a SQL Helper

Once you have defined how your database looks, you should implement methods that create and maintain the database and tables. Here are some typical statements that create and delete a table:

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    ... // Any other options for the CREATE command
    " )";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

Just like files that you save on the device's internal storage, Android stores your database in private disk space that's associated application. Your data is secure, because by default this area is not accessible to other applications.

A useful set of APIs is available in the SQLiteOpenHelper class. When you use this class to obtain references to your database, the system performs the potentially long-running operations of creating and updating the database only when needed and not during app startup. All you need to do is call getWritableDatabase() or getReadableDatabase().

Note: Because they can be long-running, be sure that you call getWritableDatabase() or getReadableDatabase() in a background thread, such as with AsyncTask or IntentService.

To use SQLiteOpenHelper, create a subclass that overrides the onCreate(), onUpgrade() and onOpen() callback methods. You may also want to implement onDowngrade(), but it's not required.

For example, here's an implementation of SQLiteOpenHelper that uses some of the commands shown above:

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

To access your database, instantiate your subclass of SQLiteOpenHelper:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

Put Information into a Database

Insert data into the database by passing a ContentValues object to the insert() method:

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content);

// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
         FeedEntry.TABLE_NAME,
         FeedEntry.COLUMN_NAME_NULLABLE,
         values);

The first argument for insert() is simply the table name. The second argument provides the name of a column in which the framework can insert NULL in the event that the ContentValues is empty (if you instead set this to "null", then the framework will not insert a row when there are no values).

Read Information from a Database

To read from a database, use the query() method, passing it your selection criteria and desired columns. The method combines elements of insert() and update(), except the column list defines the data you want to fetch, rather than the data to insert. The results of the query are returned to you in a Cursor object.

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";

Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // The table to query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don't group the rows
    null,                                     // don't filter by row groups
    sortOrder                                 // The sort order
    );

To look at a row in the cursor, use one of the Cursor move methods, which you must always call before you begin reading values. Generally, you should start by calling moveToFirst(), which places the "read position" on the first entry in the results. For each row, you can read a column's value by calling one of the Cursor get methods, such as getString() or getLong(). For each of the get methods, you must pass the index position of the column you desire, which you can get by calling getColumnIndex() or getColumnIndexOrThrow(). For example:

cursor.moveToFirst();
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

Delete Information from a Database

To delete rows from a table, you need to provide selection criteria that identify the rows. The database API provides a mechanism for creating selection criteria that protects against SQL injection. The mechanism divides the selection specification into a selection clause and selection arguments. The clause defines the columns to look at, and also allows you to combine column tests. The arguments are values to test against that are bound into the clause. Because the result isn't handled the same as a regular SQL statement, it is immune to SQL injection.

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);

Update a Database

When you need to modify a subset of your database values, use the update() method.

Updating the table combines the content values syntax of insert() with the where syntax of delete().

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the ID
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Interacting with Other Apps | Android Developers Skip to content

Most visited

Recently visited

navigation

Interacting with Other Apps

Dependencies and prerequisites

You should also read

An Android app typically has several activities. Each activity displays a user interface that allows the user to perform a specific task (such as view a map or take a photo). To take the user from one activity to another, your app must use an Intent to define your app's "intent" to do something. When you pass an Intent to the system with a method such as startActivity(), the system uses the Intent to identify and start the appropriate app component. Using intents even allows your app to start an activity that is contained in a separate app.

An Intent can be explicit in order to start a specific component (a specific Activity instance) or implicit in order to start any component that can handle the intended action (such as "capture a photo").

This class shows you how to use an Intent to perform some basic interactions with other apps, such as start another app, receive a result from that app, and make your app able to respond to intents from other apps.

Lessons

Sending the User to Another App
Shows how you can create implicit intents to launch other apps that can perform an action.
Getting a Result from an Activity
Shows how to start another activity and receive a result from the activity.
Allowing Other Apps to Start Your Activity
Shows how to make activities in your app open for use by other apps by defining intent filters that declare the implicit intents your app accepts.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Sending the User to Another App | Android Developers Skip to content

Most visited

Recently visited

navigation

Sending the User to Another App

One of Android's most important features is an app's ability to send the user to another app based on an "action" it would like to perform. For example, if your app has the address of a business that you'd like to show on a map, you don't have to build an activity in your app that shows a map. Instead, you can create a request to view the address using an Intent. The Android system then starts an app that's able to show the address on a map.

As explained in the first class, Building Your First App, you must use intents to navigate between activities in your own app. You generally do so with an explicit intent, which defines the exact class name of the component you want to start. However, when you want to have a separate app perform an action, such as "view a map," you must use an implicit intent.

This lesson shows you how to create an implicit intent for a particular action, and how to use it to start an activity that performs the action in another app.

Build an Implicit Intent

Implicit intents do not declare the class name of the component to start, but instead declare an action to perform. The action specifies the thing you want to do, such as view, edit, send, or get something. Intents often also include data associated with the action, such as the address you want to view, or the email message you want to send. Depending on the intent you want to create, the data might be a Uri, one of several other data types, or the intent might not need data at all.

If your data is a Uri, there's a simple Intent() constructor you can use to define the action and data.

For example, here's how to create an intent to initiate a phone call using the Uri data to specify the telephone number:

Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

When your app invokes this intent by calling startActivity(), the Phone app initiates a call to the given phone number.

Here are a couple other intents and their action and Uri data pairs:

  • View a map:
    // Map point based on address
    Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
    // Or map point based on latitude/longitude
    // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
    Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
    
  • View a web page:
    Uri webpage = Uri.parse("http://www.android.com");
    Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
    

Other kinds of implicit intents require "extra" data that provide different data types, such as a string. You can add one or more pieces of extra data using the various putExtra() methods.

By default, the system determines the appropriate MIME type required by an intent based on the Uri data that's included. If you don't include a Uri in the intent, you should usually use setType() to specify the type of data associated with the intent. Setting the MIME type further specifies which kinds of activities should receive the intent.

Here are some more intents that add extra data to specify the desired action:

  • Send an email with an attachment:
    Intent emailIntent = new Intent(Intent.ACTION_SEND);
    // The intent does not have a URI, so declare the "text/plain" MIME type
    emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
    emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // recipients
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
    emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
    emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));
    // You can also attach multiple items by passing an ArrayList of Uris
    
  • Create a calendar event:
    Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
    Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);
    Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());
    calendarIntent.putExtra(Events.TITLE, "Ninja class");
    calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");
    

    Note: This intent for a calendar event is supported only with API level 14 and higher.

Note: It's important that you define your Intent to be as specific as possible. For example, if you want to display an image using the ACTION_VIEW intent, you should specify a MIME type of image/*. This prevents apps that can "view" other types of data (like a map app) from being triggered by the intent.

Verify There is an App to Receive the Intent

Although the Android platform guarantees that certain intents will resolve to one of the built-in apps (such as the Phone, Email, or Calendar app), you should always include a verification step before invoking an intent.

Caution: If you invoke an intent and there is no app available on the device that can handle the intent, your app will crash.

To verify there is an activity available that can respond to the intent, call queryIntentActivities() to get a list of activities capable of handling your Intent. If the returned List is not empty, you can safely use the intent. For example:

PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

If isIntentSafe is true, then at least one app will respond to the intent. If it is false, then there aren't any apps to handle the intent.

Note: You should perform this check when your activity first starts in case you need to disable the feature that uses the intent before the user attempts to use it. If you know of a specific app that can handle the intent, you can also provide a link for the user to download the app (see how to link to your product on Google Play).

Start an Activity with the Intent

Figure 1. Example of the selection dialog that appears when more than one app can handle an intent.

Once you have created your Intent and set the extra info, call startActivity() to send it to the system. If the system identifies more than one activity that can handle the intent, it displays a dialog for the user to select which app to use, as shown in figure 1. If there is only one activity that handles the intent, the system immediately starts it.

startActivity(intent);

Here's a complete example that shows how to create an intent to view a map, verify that an app exists to handle the intent, then start it:

// Build the intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;

// Start an activity if it's safe
if (isIntentSafe) {
    startActivity(mapIntent);
}

Show an App Chooser

Figure 2. A chooser dialog.

Notice that when you start an activity by passing your Intent to startActivity() and there is more than one app that responds to the intent, the user can select which app to use by default (by selecting a checkbox at the bottom of the dialog; see figure 1). This is nice when performing an action for which the user generally wants to use the same app every time, such as when opening a web page (users likely use just one web browser) or taking a photo (users likely prefer one camera).

However, if the action to be performed could be handled by multiple apps and the user might prefer a different app each time—such as a "share" action, for which users might have several apps through which they might share an item—you should explicitly show a chooser dialog as shown in figure 2. The chooser dialog forces the user to select which app to use for the action every time (the user cannot select a default app for the action).

To show the chooser, create an Intent using createChooser() and pass it to startActivity(). For example:

Intent intent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);

// Verify the intent will resolve to at least one activity
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

This displays a dialog with a list of apps that respond to the intent passed to the createChooser() method and uses the supplied text as the dialog title.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Getting a Result from an Activity | Android Developers Skip to content

Most visited

Recently visited

navigation

Getting a Result from an Activity

This lesson teaches you to

  1. Start the Activity
  2. Receive the Result

You should also read

Starting another activity doesn't have to be one-way. You can also start another activity and receive a result back. To receive a result, call startActivityForResult() (instead of startActivity()).

For example, your app can start a camera app and receive the captured photo as a result. Or, you might start the People app in order for the user to select a contact and you'll receive the contact details as a result.

Of course, the activity that responds must be designed to return a result. When it does, it sends the result as another Intent object. Your activity receives it in the onActivityResult() callback.

Note: You can use explicit or implicit intents when you call startActivityForResult(). When starting one of your own activities to receive a result, you should use an explicit intent to ensure that you receive the expected result.

Start the Activity

There's nothing special about the Intent object you use when starting an activity for a result, but you do need to pass an additional integer argument to the startActivityForResult() method.

The integer argument is a "request code" that identifies your request. When you receive the result Intent, the callback provides the same request code so that your app can properly identify the result and determine how to handle it.

For example, here's how to start an activity that allows the user to pick a contact:

static final int PICK_CONTACT_REQUEST = 1;  // The request code
...
private void pickContact() {
    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
    pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}

Receive the Result

When the user is done with the subsequent activity and returns, the system calls your activity's onActivityResult() method. This method includes three arguments:

  • The request code you passed to startActivityForResult().
  • A result code specified by the second activity. This is either RESULT_OK if the operation was successful or RESULT_CANCELED if the user backed out or the operation failed for some reason.
  • An Intent that carries the result data.

For example, here's how you can handle the result for the "pick a contact" intent:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request we're responding to
    if (requestCode == PICK_CONTACT_REQUEST) {
        // Make sure the request was successful
        if (resultCode == RESULT_OK) {
            // The user picked a contact.
            // The Intent's data Uri identifies which contact was selected.

            // Do something with the contact here (bigger example below)
        }
    }
}

In this example, the result Intent returned by Android's Contacts or People app provides a content Uri that identifies the contact the user selected.

In order to successfully handle the result, you must understand what the format of the result Intent will be. Doing so is easy when the activity returning a result is one of your own activities. Apps included with the Android platform offer their own APIs that you can count on for specific result data. For instance, the People app always returns a result with the content URI that identifies the selected contact, and the Camera app returns a Bitmap in the "data" extra (see the class about Capturing Photos).

Bonus: Read the contact data

The code above showing how to get a result from the People app doesn't go into details about how to actually read the data from the result, because it requires more advanced discussion about content providers. However, if you're curious, here's some more code that shows how to query the result data to get the phone number from the selected contact:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request it is that we're responding to
    if (requestCode == PICK_CONTACT_REQUEST) {
        // Make sure the request was successful
        if (resultCode == RESULT_OK) {
            // Get the URI that points to the selected contact
            Uri contactUri = data.getData();
            // We only need the NUMBER column, because there will be only one row in the result
            String[] projection = {Phone.NUMBER};

            // Perform the query on the contact to get the NUMBER column
            // We don't need a selection or sort order (there's only one result for the given URI)
            // CAUTION: The query() method should be called from a separate thread to avoid blocking
            // your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
            // Consider using CursorLoader to perform the query.
            Cursor cursor = getContentResolver()
                    .query(contactUri, projection, null, null, null);
            cursor.moveToFirst();

            // Retrieve the phone number from the NUMBER column
            int column = cursor.getColumnIndex(Phone.NUMBER);
            String number = cursor.getString(column);

            // Do something with the phone number...
        }
    }
}

Note: Before Android 2.3 (API level 9), performing a query on the Contacts Provider (like the one shown above) requires that your app declare the READ_CONTACTS permission (see Security and Permissions). However, beginning with Android 2.3, the Contacts/People app grants your app a temporary permission to read from the Contacts Provider when it returns you a result. The temporary permission applies only to the specific contact requested, so you cannot query a contact other than the one specified by the intent's Uri, unless you do declare the READ_CONTACTS permission.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Allowing Other Apps to Start Your Activity | Android Developers Skip to content

Most visited

Recently visited

navigation

Allowing Other Apps to Start Your Activity

The previous two lessons focused on one side of the story: starting another app's activity from your app. But if your app can perform an action that might be useful to another app, your app should be prepared to respond to action requests from other apps. For instance, if you build a social app that can share messages or photos with the user's friends, it's in your best interest to support the ACTION_SEND intent so users can initiate a "share" action from another app and launch your app to perform the action.

To allow other apps to start your activity, you need to add an <intent-filter> element in your manifest file for the corresponding <activity> element.

When your app is installed on a device, the system identifies your intent filters and adds the information to an internal catalog of intents supported by all installed apps. When an app calls startActivity() or startActivityForResult(), with an implicit intent, the system finds which activity (or activities) can respond to the intent.

Add an Intent Filter

In order to properly define which intents your activity can handle, each intent filter you add should be as specific as possible in terms of the type of action and data the activity accepts.

The system may send a given Intent to an activity if that activity has an intent filter fulfills the following criteria of the Intent object:

Action
A string naming the action to perform. Usually one of the platform-defined values such as ACTION_SEND or ACTION_VIEW.

Specify this in your intent filter with the <action> element. The value you specify in this element must be the full string name for the action, instead of the API constant (see the examples below).

Data
A description of the data associated with the intent.

Specify this in your intent filter with the <data> element. Using one or more attributes in this element, you can specify just the MIME type, just a URI prefix, just a URI scheme, or a combination of these and others that indicate the data type accepted.

Note: If you don't need to declare specifics about the data Uri (such as when your activity handles to other kind of "extra" data, instead of a URI), you should specify only the android:mimeType attribute to declare the type of data your activity handles, such as text/plain or image/jpeg.

Category
Provides an additional way to characterize the activity handling the intent, usually related to the user gesture or location from which it's started. There are several different categories supported by the system, but most are rarely used. However, all implicit intents are defined with CATEGORY_DEFAULT by default.

Specify this in your intent filter with the <category> element.

In your intent filter, you can declare which criteria your activity accepts by declaring each of them with corresponding XML elements nested in the <intent-filter> element.

For example, here's an activity with an intent filter that handles the ACTION_SEND intent when the data type is either text or an image:

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
        <data android:mimeType="image/*"/>
    </intent-filter>
</activity>

Each incoming intent specifies only one action and one data type, but it's OK to declare multiple instances of the <action>, <category>, and <data> elements in each <intent-filter>.

If any two pairs of action and data are mutually exclusive in their behaviors, you should create separate intent filters to specify which actions are acceptable when paired with which data types.

For example, suppose your activity handles both text and images for both the ACTION_SEND and ACTION_SENDTO intents. In this case, you must define two separate intent filters for the two actions because a ACTION_SENDTO intent must use the data Uri to specify the recipient's address using the send or sendto URI scheme. For example:

<activity android:name="ShareActivity">
    <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
    <intent-filter>
        <action android:name="android.intent.action.SENDTO"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="sms" />
        <data android:scheme="smsto" />
    </intent-filter>
    <!-- filter for sending text or images; accepts SEND action and text or image data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

Note: In order to receive implicit intents, you must include the CATEGORY_DEFAULT category in the intent filter. The methods startActivity() and startActivityForResult() treat all intents as if they declared the CATEGORY_DEFAULT category. If you do not declare it in your intent filter, no implicit intents will resolve to your activity.

For more information about sending and receiving ACTION_SEND intents that perform social sharing behaviors, see the lesson about Receiving Simple Data from Other Apps.

Handle the Intent in Your Activity

In order to decide what action to take in your activity, you can read the Intent that was used to start it.

As your activity starts, call getIntent() to retrieve the Intent that started the activity. You can do so at any time during the lifecycle of the activity, but you should generally do so during early callbacks such as onCreate() or onStart().

For example:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    // Get the intent that started this activity
    Intent intent = getIntent();
    Uri data = intent.getData();

    // Figure out what to do based on the intent type
    if (intent.getType().indexOf("image/") != -1) {
        // Handle intents with image data ...
    } else if (intent.getType().equals("text/plain")) {
        // Handle intents with text ...
    }
}

Return a Result

If you want to return a result to the activity that invoked yours, simply call setResult() to specify the result code and result Intent. When your operation is done and the user should return to the original activity, call finish() to close (and destroy) your activity. For example:

// Create intent to deliver some kind of result data
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"));
setResult(Activity.RESULT_OK, result);
finish();

You must always specify a result code with the result. Generally, it's either RESULT_OK or RESULT_CANCELED. You can then provide additional data with an Intent, as necessary.

Note: The result is set to RESULT_CANCELED by default. So, if the user presses the Back button before completing the action and before you set the result, the original activity receives the "canceled" result.

If you simply need to return an integer that indicates one of several result options, you can set the result code to any value higher than 0. If you use the result code to deliver an integer and you have no need to include the Intent, you can call setResult() and pass only a result code. For example:

setResult(RESULT_COLOR_RED);
finish();

In this case, there might be only a handful of possible results, so the result code is a locally defined integer (greater than 0). This works well when you're returning a result to an activity in your own app, because the activity that receives the result can reference the public constant to determine the value of the result code.

Note: There's no need to check whether your activity was started with startActivity() or startActivityForResult(). Simply call setResult() if the intent that started your activity might expect a result. If the originating activity had called startActivityForResult(), then the system delivers it the result you supply to setResult(); otherwise, the result is ignored.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Working with System Permissions | Android Developers Skip to content

Most visited

Recently visited

navigation

Working with System Permissions

Design Patterns

Permissions

To protect the system's integrity and the user's privacy, Android runs each app in a limited access sandbox. If the app wants to use resources or information outside of its sandbox, the app has to explicitly request permission. Depending on the type of permission the app requests, the system may grant the permission automatically, or the system may ask the user to grant the permission.

This class shows you how to declare and request permissions for your app.

Lessons

Declaring Permissions
Learn how to declare the permissions you need in your app manifest.
Requesting Permissions at Run Time
Learn how to request permissions from the user while the app is running. This lesson only applies to apps on devices running Android 6.0 (API level 23) and higher.
Permissions Best Practices
Guidelines for creating the best user experience in requesting and using permissions.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Building Apps with Content Sharing | Android Developers Skip to content

Most visited

Recently visited

navigation

Building Apps with Content Sharing

These classes teach you how to create apps that share data between apps and devices.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Sharing Simple Data | Android Developers Skip to content

Most visited

Recently visited

navigation

Sharing Simple Data

Dependencies and prerequisites

One of the great things about Android applications is their ability to communicate and integrate with each other. Why reinvent functionality that isn't core to your application when it already exists in another application?

This class covers some common ways you can send and receive simple data between applications using Intent APIs and the ActionProvider object.

Lessons

Sending Simple Data to Other Apps
Learn how to set up your application to be able to send text and binary data to other applications with intents.
Receiving Simple Data from Other Apps
Learn how to set up your application to receive text and binary data from intents.
Adding an Easy Share Action
Learn how to add a "share" action item to your action bar.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Sending Simple Data to Other Apps | Android Developers Skip to content

Most visited

Recently visited

navigation

Sending Simple Data to Other Apps

When you construct an intent, you must specify the action you want the intent to "trigger." Android defines several actions, including ACTION_SEND which, as you can probably guess, indicates that the intent is sending data from one activity to another, even across process boundaries. To send data to another activity, all you need to do is specify the data and its type, the system will identify compatible receiving activities and display them to the user (if there are multiple options) or immediately start the activity (if there is only one option). Similarly, you can advertise the data types that your activities support receiving from other applications by specifying them in your manifest.

Sending and receiving data between applications with intents is most commonly used for social sharing of content. Intents allow users to share information quickly and easily, using their favorite applications.

Note: The best way to add a share action item to an ActionBar is to use ShareActionProvider, which became available in API level 14. ShareActionProvider is discussed in the lesson about Adding an Easy Share Action.

Send Text Content

Figure 1. Screenshot of ACTION_SEND intent chooser on a handset.

The most straightforward and common use of the ACTION_SEND action is sending text content from one activity to another. For example, the built-in Browser app can share the URL of the currently-displayed page as text with any application. This is useful for sharing an article or website with friends via email or social networking. Here is the code to implement this type of sharing:

Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(sendIntent);

If there's an installed application with a filter that matches ACTION_SEND and MIME type text/plain, the Android system will run it; if more than one application matches, the system displays a disambiguation dialog (a "chooser") that allows the user to choose an app.

However, if you call Intent.createChooser(), passing it your Intent object, it returns a version of your intent that will always display the chooser. This has some advantages:

  • Even if the user has previously selected a default action for this intent, the chooser will still be displayed.
  • If no applications match, Android displays a system message.
  • You can specify a title for the chooser dialog.

Here's the updated code:

Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));

The resulting dialog is shown in figure 1.

Optionally, you can set some standard extras for the intent: EXTRA_EMAIL, EXTRA_CC, EXTRA_BCC, EXTRA_SUBJECT. If the receiving application is not designed to use them, it simply ignores them.

Note: Some e-mail applications, such as Gmail, expect a String[] for extras like EXTRA_EMAIL and EXTRA_CC, use putExtra(String, String[]) to add these to your intent.

Send Binary Content

Binary data is shared using the ACTION_SEND action combined with setting the appropriate MIME type and placing the URI to the data in an extra named EXTRA_STREAM. This is commonly used to share an image but can be used to share any type of binary content:

Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uriToImage);
shareIntent.setType("image/jpeg");
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));

Note the following:

  • You can use a MIME type of "*/*", but this will only match activities that are able to handle generic data streams.
  • The receiving application needs permission to access the data the Uri points to. The recommended ways to do this are:
    • Store the data in your own ContentProvider, making sure that other apps have the correct permission to access your provider. The preferred mechanism for providing access is to use per-URI permissions which are temporary and only grant access to the receiving application. An easy way to create a ContentProvider like this is to use the FileProvider helper class.
    • Use the system MediaStore. The MediaStore is primarily aimed at video, audio and image MIME types, however beginning with Android 3.0 (API level 11) it can also store non-media types (see MediaStore.Files for more info). Files can be inserted into the MediaStore using scanFile() after which a content:// style Uri suitable for sharing is passed to the provided onScanCompleted() callback. Note that once added to the system MediaStore the content is accessible to any app on the device.

Send Multiple Pieces of Content

To share multiple pieces of content, use the ACTION_SEND_MULTIPLE action together with a list of URIs pointing to the content. The MIME type varies according to the mix of content you're sharing. For example, if you share 3 JPEG images, the type is still "image/jpeg". For a mixture of image types, it should be "image/*" to match an activity that handles any type of image. You should only use "*/*" if you're sharing out a wide variety of types. As previously stated, it's up to the receiving application to parse and process your data. Here's an example:

ArrayList<Uri> imageUris = new ArrayList<Uri>();
imageUris.add(imageUri1); // Add your image URIs here
imageUris.add(imageUri2);

Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
startActivity(Intent.createChooser(shareIntent, "Share images to.."));

As before, make sure the provided URIs point to data that a receiving application can access.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Receiving Simple Data from Other Apps | Android Developers Skip to content

Most visited

Recently visited

navigation

Receiving Simple Data from Other Apps

This lesson teaches you to

  1. Update Your Manifest
  2. Handle the Incoming Content

You should also read

Just as your application can send data to other applications, so too can it easily receive data from applications. Think about how users interact with your application, and what data types you want to receive from other applications. For example, a social networking application would likely be interested in receiving text content, like an interesting web URL, from another app. The Google+ Android application accepts both text and single or multiple images. With this app, a user can easily start a new Google+ post with photos from the Android Gallery app.

Update Your Manifest

Intent filters inform the system what intents an application component is willing to accept. Similar to how you constructed an intent with action ACTION_SEND in the Sending Simple Data to Other Apps lesson, you create intent filters in order to be able to receive intents with this action. You define an intent filter in your manifest, using the <intent-filter> element. For example, if your application handles receiving text content, a single image of any type, or multiple images of any type, your manifest would look like:

<activity android:name=".ui.MyActivity" >
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>

Note: For more information on intent filters and intent resolution please read Intents and Intent Filters

When another application tries to share any of these things by constructing an intent and passing it to startActivity(), your application will be listed as an option in the intent chooser. If the user selects your application, the corresponding activity (.ui.MyActivity in the example above) will be started. It is then up to you to handle the content appropriately within your code and UI.

Handle the Incoming Content

To handle the content delivered by an Intent, start by calling getIntent() to get Intent object. Once you have the object, you can examine its contents to determine what to do next. Keep in mind that if this activity can be started from other parts of the system, such as the launcher, then you will need to take this into consideration when examining the intent.

void onCreate (Bundle savedInstanceState) {
    ...
    // Get intent, action and MIME type
    Intent intent = getIntent();
    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) && type != null) {
        if ("text/plain".equals(type)) {
            handleSendText(intent); // Handle text being sent
        } else if (type.startsWith("image/")) {
            handleSendImage(intent); // Handle single image being sent
        }
    } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
        if (type.startsWith("image/")) {
            handleSendMultipleImages(intent); // Handle multiple images being sent
        }
    } else {
        // Handle other intents, such as being started from the home screen
    }
    ...
}

void handleSendText(Intent intent) {
    String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
    if (sharedText != null) {
        // Update UI to reflect text being shared
    }
}

void handleSendImage(Intent intent) {
    Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
    if (imageUri != null) {
        // Update UI to reflect image being shared
    }
}

void handleSendMultipleImages(Intent intent) {
    ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
    if (imageUris != null) {
        // Update UI to reflect multiple images being shared
    }
}

Caution: Take extra care to check the incoming data, you never know what some other application may send you. For example, the wrong MIME type might be set, or the image being sent might be extremely large. Also, remember to process binary data in a separate thread rather than the main ("UI") thread.

Updating the UI can be as simple as populating an EditText, or it can be more complicated like applying an interesting photo filter to an image. It's really specific to your application what happens next.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Adding an Easy Share Action | Android Developers Skip to content

Most visited

Recently visited

navigation

Adding an Easy Share Action

This lesson teaches you to

  1. Update Menu Declarations
  2. Set the Share Intent

You should also read

Implementing an effective and user friendly share action in your ActionBar is made even easier with the introduction of ActionProvider in Android 4.0 (API Level 14). An ActionProvider, once attached to a menu item in the action bar, handles both the appearance and behavior of that item. In the case of ShareActionProvider, you provide a share intent and it does the rest.

Note:  ShareActionProvider is available starting with API Level 14 and higher.

Figure 1. The ShareActionProvider in the Gallery app.

Update Menu Declarations

To get started with ShareActionProviders, define the android:actionProviderClass attribute for the corresponding <item> in your menu resource file:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:id="@+id/menu_item_share"
            android:showAsAction="ifRoom"
            android:title="Share"
            android:actionProviderClass=
                "android.widget.ShareActionProvider" />
    ...
</menu>

This delegates responsibility for the item's appearance and function to ShareActionProvider. However, you will need to tell the provider what you would like to share.

Set the Share Intent

In order for ShareActionProvider to function, you must provide it a share intent. This share intent should be the same as described in the Sending Simple Data to Other Apps lesson, with action ACTION_SEND and additional data set via extras like EXTRA_TEXT and EXTRA_STREAM. To assign a share intent, first find the corresponding MenuItem while inflating your menu resource in your Activity or Fragment. Next, call MenuItem.getActionProvider() to retrieve an instance of ShareActionProvider. Use setShareIntent() to update the share intent associated with that action item. Here's an example:

private ShareActionProvider mShareActionProvider;
...

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate menu resource file.
    getMenuInflater().inflate(R.menu.share_menu, menu);

    // Locate MenuItem with ShareActionProvider
    MenuItem item = menu.findItem(R.id.menu_item_share);

    // Fetch and store ShareActionProvider
    mShareActionProvider = (ShareActionProvider) item.getActionProvider();

    // Return true to display menu
    return true;
}

// Call to update the share intent
private void setShareIntent(Intent shareIntent) {
    if (mShareActionProvider != null) {
        mShareActionProvider.setShareIntent(shareIntent);
    }
}

You may only need to set the share intent once during the creation of your menus, or you may want to set it and then update it as the UI changes. For example, when you view photos full screen in the Gallery app, the sharing intent changes as you flip between photos.

For further discussion about the ShareActionProvider object, see Action Views and Action Providers.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Sharing Files | Android Developers Skip to content

Most visited

Recently visited

navigation

Sharing Files

Dependencies and prerequisites

  • Android 1.6 (API Level 4) or higher
  • Familiarity with file operations such as opening, reading, and writing files

You should also read

Apps often have a need to offer one or more of their files to another app. For example, an image gallery may want to offer files to image editors, or a file management app may want to allow users to copy and paste files between areas in external storage. One way a sending app can share a file is to respond to a request from the receiving app.

In all cases, the only secure way to offer a file from your app to another app is to send the receiving app the file's content URI and grant temporary access permissions to that URI. Content URIs with temporary URI access permissions are secure because they apply only to the app that receives the URI, and they expire automatically. The Android FileProvider component provides the method getUriForFile() for generating a file's content URI.

If you want to share small amounts of text or numeric data between apps, you should send an Intent that contains the data. To learn how to send simple data with an Intent, see the training class Sharing Simple Data.

This class explains how to securely share files from your app to another app using content URIs generated by the Android FileProvider component and temporary permissions that you grant to the receiving app for the content URI.

Lessons

Setting Up File Sharing
Learn how to set up your app to share files.
Sharing a File
Learn how to offer a file to another app by generating a content URI for the file, granting access permissions to the URI, and sending the URI to the app.
Requesting a Shared File
Learn how to request a file shared by another app, receive the content URI for the file, and use the content URI to open the file.
Retrieving File Information
Learn how an app can use a content URI generated by a FileProvider to retrieve file information including MIME type and file size.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Setting Up File Sharing | Android Developers Skip to content

Most visited

Recently visited

navigation

Setting Up File Sharing

This lesson teaches you to

  1. Specify the FileProvider
  2. Specify Sharable Directories

You should also read

To securely offer a file from your app to another app, you need to configure your app to offer a secure handle to the file, in the form of a content URI. The Android FileProvider component generates content URIs for files, based on specifications you provide in XML. This lesson shows you how to add the default implementation of FileProvider to your app, and how to specify the files you want to offer to other apps.

Note: The FileProvider class is part of the v4 Support Library. For information about including this library in your application, see Support Library Setup.

Specify the FileProvider

Defining a FileProvider for your app requires an entry in your manifest. This entry specifies the authority to use in generating content URIs, as well as the name of an XML file that specifies the directories your app can share.

The following snippet shows you how to add to your manifest the <provider> element that specifies the FileProvider class, the authority, and the XML file name:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <application
        ...>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>
        ...
    </application>
</manifest>

In this example, the android:authorities attribute specifies the URI authority that you want to use for content URIs generated by the FileProvider. In the example, the authority is com.example.myapp.fileprovider. For your own app, specify an authority consisting of the app's android:package value with the string "fileprovider" appended to it. To learn more about the authority value, see the topic Content URIs and the documentation for the android:authorities attribute.

The <meta-data> child element of the <provider> points to an XML file that specifies the directories you want to share. The android:resource attribute is the path and name of the file, without the .xml extension.The contents of this file are described in the next section.

Specify Sharable Directories

Once you have added the FileProvider to your app manifest, you need to specify the directories that contain the files you want to share. To specify the directories, start by creating the file filepaths.xml in the res/xml/ subdirectory of your project. In this file, specify the directories by adding an XML element for each directory. The following snippet shows you an example of the contents of res/xml/filepaths.xml. The snippet also demonstrates how to share a subdirectory of the files/ directory in your internal storage area:

<paths>
    <files-path path="images/" name="myimages" />
</paths>

In this example, the <files-path> tag shares directories within the files/ directory of your app's internal storage. The path attribute shares the images/ subdirectory of files/. The name attribute tells the FileProvider to add the path segment myimages to content URIs for files in the files/images/ subdirectory.

The <paths> element can have multiple children, each specifying a different directory to share. In addition to the <files-path> element, you can use the <external-path> element to share directories in external storage, and the <cache-path> element to share directories in your internal cache directory. To learn more about the child elements that specify shared directories, see the FileProvider reference documentation.

Note: The XML file is the only way you can specify the directories you want to share; you can't programmatically add a directory.

You now have a complete specification of a FileProvider that generates content URIs for files in the files/ directory of your app's internal storage or for files in subdirectories of files/. When your app generates a content URI for a file, it contains the authority specified in the <provider> element (com.example.myapp.fileprovider), the path myimages/, and the name of the file.

For example, if you define a FileProvider according to the snippets in this lesson, and you request a content URI for the file default_image.jpg, FileProvider returns the following URI:

content://com.example.myapp.fileprovider/myimages/default_image.jpg
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Sharing a File | Android Developers Skip to content

Most visited

Recently visited

navigation

Sharing a File

Once you have set up your app to share files using content URIs, you can respond to other apps' requests for those files. One way to respond to these requests is to provide a file selection interface from the server app that other applications can invoke. This approach allows a client application to let users select a file from the server app and then receive the selected file's content URI.

This lesson shows you how to create a file selection Activity in your app that responds to requests for files.

Receive File Requests

To receive requests for files from client apps and respond with a content URI, your app should provide a file selection Activity. Client apps start this Activity by calling startActivityForResult() with an Intent containing the action ACTION_PICK. When the client app calls startActivityForResult(), your app can return a result to the client app, in the form of a content URI for the file the user selected.

To learn how to implement a request for a file in a client app, see the lesson Requesting a Shared File.

Create a File Selection Activity

To set up the file selection Activity, start by specifying the Activity in your manifest, along with an intent filter that matches the action ACTION_PICK and the categories CATEGORY_DEFAULT and CATEGORY_OPENABLE. Also add MIME type filters for the files your app serves to other apps. The following snippet shows you how to specify the new Activity and intent filter:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
        <application>
        ...
            <activity
                android:name=".FileSelectActivity"
                android:label="@File Selector" >
                <intent-filter>
                    <action
                        android:name="android.intent.action.PICK"/>
                    <category
                        android:name="android.intent.category.DEFAULT"/>
                    <category
                        android:name="android.intent.category.OPENABLE"/>
                    <data android:mimeType="text/plain"/>
                    <data android:mimeType="image/*"/>
                </intent-filter>
            </activity>

Define the file selection Activity in code

Next, define an Activity subclass that displays the files available from your app's files/images/ directory in internal storage and allows the user to pick the desired file. The following snippet demonstrates how to define this Activity and respond to the user's selection:

public class MainActivity extends Activity {
    // The path to the root of this app's internal storage
    private File mPrivateRootDir;
    // The path to the "images" subdirectory
    private File mImagesDir;
    // Array of files in the images subdirectory
    File[] mImageFiles;
    // Array of filenames corresponding to mImageFiles
    String[] mImageFilenames;
    // Initialize the Activity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Set up an Intent to send back to apps that request a file
        mResultIntent =
                new Intent("com.example.myapp.ACTION_RETURN_FILE");
        // Get the files/ subdirectory of internal storage
        mPrivateRootDir = getFilesDir();
        // Get the files/images subdirectory;
        mImagesDir = new File(mPrivateRootDir, "images");
        // Get the files in the images subdirectory
        mImageFiles = mImagesDir.listFiles();
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null);
        /*
         * Display the file names in the ListView mFileListView.
         * Back the ListView with the array mImageFilenames, which
         * you can create by iterating through mImageFiles and
         * calling File.getAbsolutePath() for each File
         */
         ...
    }
    ...
}

Respond to a File Selection

Once a user selects a shared file, your application must determine what file was selected and then generate a content URI for the file. Since the Activity displays the list of available files in a ListView, when the user clicks a file name the system calls the method onItemClick(), in which you can get the selected file.

In onItemClick(), get a File object for the file name of the selected file and pass it as an argument to getUriForFile(), along with the authority that you specified in the <provider> element for the FileProvider. The resulting content URI contains the authority, a path segment corresponding to the file's directory (as specified in the XML meta-data), and the name of the file including its extension. How FileProvider maps directories to path segments based on XML meta-data is described in the section Specify Sharable Directories.

The following snippet shows you how to detect the selected file and get a content URI for it:

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        mFileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            /*
             * When a filename in the ListView is clicked, get its
             * content URI and send it to the requesting app
             */
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                /*
                 * Get a File for the selected file name.
                 * Assume that the file names are in the
                 * mImageFilename array.
                 */
                File requestFile = new File(mImageFilename[position]);
                /*
                 * Most file-related method calls need to be in
                 * try-catch blocks.
                 */
                // Use the FileProvider to get a content URI
                try {
                    fileUri = FileProvider.getUriForFile(
                            MainActivity.this,
                            "com.example.myapp.fileprovider",
                            requestFile);
                } catch (IllegalArgumentException e) {
                    Log.e("File Selector",
                          "The selected file can't be shared: " +
                          clickedFilename);
                }
                ...
            }
        });
        ...
    }

Remember that you can only generate content URIs for files that reside in a directory you've specified in the meta-data file that contains the <paths> element, as described in the section Specify Sharable Directories. If you call getUriForFile() for a File in a path that you haven't specified, you receive an IllegalArgumentException.

Grant Permissions for the File

Now that you have a content URI for the file you want to share with another app, you need to allow the client app to access the file. To allow access, grant permissions to the client app by adding the content URI to an Intent and then setting permission flags on the Intent. The permissions you grant are temporary and expire automatically when the receiving app's task stack is finished.

The following code snippet shows you how to set read permission for the file:

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks in the ListView
        mFileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    // Grant temporary read permission to the content URI
                    mResultIntent.addFlags(
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }
                ...
             }
             ...
        });
    ...
    }

Caution: Calling setFlags() is the only way to securely grant access to your files using temporary access permissions. Avoid calling Context.grantUriPermission() method for a file's content URI, since this method grants access that you can only revoke by calling Context.revokeUriPermission().

Share the File with the Requesting App

To share the file with the app that requested it, pass the Intent containing the content URI and permissions to setResult(). When the Activity you have just defined is finished, the system sends the Intent containing the content URI to the client app. The following code snippet shows you how to do this:

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        mFileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    ...
                    // Put the Uri and MIME type in the result Intent
                    mResultIntent.setDataAndType(
                            fileUri,
                            getContentResolver().getType(fileUri));
                    // Set the result
                    MainActivity.this.setResult(Activity.RESULT_OK,
                            mResultIntent);
                    } else {
                        mResultIntent.setDataAndType(null, "");
                        MainActivity.this.setResult(RESULT_CANCELED,
                                mResultIntent);
                    }
                }
        });

Provide users with an way to return immediately to the client app once they have chosen a file. One way to do this is to provide a checkmark or Done button. Associate a method with the button using the button's android:onClick attribute. In the method, call finish(). For example:

    public void onDoneClick(View v) {
        // Associate a method with the Done button
        finish();
    }
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Requesting a Shared File | Android Developers Skip to content

Most visited

Recently visited

navigation

Requesting a Shared File

When an app wants to access a file shared by another app, the requesting app (the client) usually sends a request to the app sharing the files (the server). In most cases, the request starts an Activity in the server app that displays the files it can share. The user picks a file, after which the server app returns the file's content URI to the client app.

This lesson shows you how a client app requests a file from a server app, receives the file's content URI from the server app, and opens the file using the content URI.

Send a Request for the File

To request a file from the server app, the client app calls startActivityForResult with an Intent containing the action such as ACTION_PICK and a MIME type that the client app can handle.

For example, the following code snippet demonstrates how to send an Intent to a server app in order to start the Activity described in Sharing a File:

public class MainActivity extends Activity {
    private Intent mRequestFileIntent;
    private ParcelFileDescriptor mInputPFD;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRequestFileIntent = new Intent(Intent.ACTION_PICK);
        mRequestFileIntent.setType("image/jpg");
        ...
    }
    ...
    protected void requestFile() {
        /**
         * When the user requests a file, send an Intent to the
         * server app.
         * files.
         */
            startActivityForResult(mRequestFileIntent, 0);
        ...
    }
    ...
}

Access the Requested File

The server app sends the file's content URI back to the client app in an Intent. This Intent is passed to the client app in its override of onActivityResult(). Once the client app has the file's content URI, it can access the file by getting its FileDescriptor.

File security is preserved in this process because the content URI is the only piece of data that the client app receives. Since this URI doesn't contain a directory path, the client app can't discover and open any other files in the server app. Only the client app gets access to the file, and only for the permissions granted by the server app. The permissions are temporary, so once the client app's task stack is finished, the file is no longer accessible outside the server app.

The next snippet demonstrates how the client app handles the Intent sent from the server app, and how the client app gets the FileDescriptor using the content URI:

    /*
     * When the Activity of the app that hosts files sets a result and calls
     * finish(), this method is invoked. The returned Intent contains the
     * content URI of a selected file. The result code indicates if the
     * selection worked or not.
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode,
            Intent returnIntent) {
        // If the selection didn't work
        if (resultCode != RESULT_OK) {
            // Exit without doing anything else
            return;
        } else {
            // Get the file's content URI from the incoming Intent
            Uri returnUri = returnIntent.getData();
            /*
             * Try to open the file for "read" access using the
             * returned URI. If the file isn't found, write to the
             * error log and return.
             */
            try {
                /*
                 * Get the content resolver instance for this context, and use it
                 * to get a ParcelFileDescriptor for the file.
                 */
                mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                Log.e("MainActivity", "File not found.");
                return;
            }
            // Get a regular file descriptor for the file
            FileDescriptor fd = mInputPFD.getFileDescriptor();
            ...
        }
    }

The method openFileDescriptor() returns a ParcelFileDescriptor for the file. From this object, the client app gets a FileDescriptor object, which it can then use to read the file.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Retrieving File Information | Android Developers Skip to content

Most visited

Recently visited

navigation

Retrieving File Information

Before a client app tries to work with a file for which it has a content URI, the app can request information about the file from the server app, including the file's data type and file size. The data type helps the client app to determine if it can handle the file, and the file size helps the client app set up buffering and caching for the file.

This lesson demonstrates how to query the server app's FileProvider to retrieve a file's MIME type and size.

Retrieve a File's MIME Type

A file's data type indicates to the client app how it should handle the file's contents. To get the data type of a shared file given its content URI, the client app calls ContentResolver.getType(). This method returns the file's MIME type. By default, a FileProvider determines the file's MIME type from its filename extension.

The following code snippet demonstrates how a client app retrieves the MIME type of a file once the server app has returned the content URI to the client:

    ...
    /*
     * Get the file's content URI from the incoming Intent, then
     * get the file's MIME type
     */
    Uri returnUri = returnIntent.getData();
    String mimeType = getContentResolver().getType(returnUri);
    ...

Retrieve a File's Name and Size

The FileProvider class has a default implementation of the query() method that returns the name and size of the file associated with a content URI in a Cursor. The default implementation returns two columns:

DISPLAY_NAME
The file's name, as a String. This value is the same as the value returned by File.getName().
SIZE
The size of the file in bytes, as a long This value is the same as the value returned by File.length()

The client app can get both the DISPLAY_NAME and SIZE for a file by setting all of the arguments of query() to null except for the content URI. For example, this code snippet retrieves a file's DISPLAY_NAME and SIZE and displays each one in separate TextView:

    ...
    /*
     * Get the file's content URI from the incoming Intent,
     * then query the server app to get the file's display name
     * and size.
     */
    Uri returnUri = returnIntent.getData();
    Cursor returnCursor =
            getContentResolver().query(returnUri, null, null, null, null);
    /*
     * Get the column indexes of the data in the Cursor,
     * move to the first row in the Cursor, get the data,
     * and display it.
     */
    int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
    int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
    returnCursor.moveToFirst();
    TextView nameView = (TextView) findViewById(R.id.filename_text);
    TextView sizeView = (TextView) findViewById(R.id.filesize_text);
    nameView.setText(returnCursor.getString(nameIndex));
    sizeView.setText(Long.toString(returnCursor.getLong(sizeIndex)));
    ...
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Sharing Files with NFC | Android Developers Skip to content

Most visited

Recently visited

navigation

Sharing Files with NFC

Dependencies and prerequisites

  • Android 4.1 (API Level 16) or higher
  • At least two NFC-enabled Android devices (NFC is not supported in the emulator)

You should also read

Android allows you to transfer large files between devices using the Android Beam file transfer feature. This feature has a simple API and allows users to start the transfer process by simply touching devices. In response, Android Beam file transfer automatically copies files from one device to the other and notifies the user when it's finished.

While the Android Beam file transfer API handles large amounts of data, the Android Beam NDEF transfer API introduced in Android 4.0 (API level 14) handles small amounts of data such as URIs or other small messages. In addition, Android Beam is only one of the features available in the Android NFC framework, which allows you to read NDEF messages from NFC tags. To learn more about Android Beam, see the topic Beaming NDEF Messages to Other Devices. To learn more about the NFC framework, see the Near Field Communication API guide.

Lessons

Sending Files to Another Device
Learn how to set up your app to send files to another device.
Receiving Files from Another Device
Learn how to set up your app to receive files sent by another device.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Sending Files to Another Device | Android Developers Skip to content

Most visited

Recently visited

navigation

Sending Files to Another Device

This lesson shows you how to design your app to send large files to another device using Android Beam file transfer. To send files, you request permission to use NFC and external storage, test to ensure your device supports NFC, and provide URIs to Android Beam file transfer.

The Android Beam file transfer feature has the following requirements:

  1. Android Beam file transfer for large files is only available in Android 4.1 (API level 16) and higher.
  2. Files you want to transfer must reside in external storage. To learn more about using external storage, read Using the External Storage.
  3. Each file you want to transfer must be world-readable. You can set this permission by calling the method File.setReadable(true,false).
  4. You must provide a file URI for the files you want to transfer. Android Beam file transfer is unable to handle content URIs generated by FileProvider.getUriForFile.

Declare Features in the Manifest

First, edit your app manifest to declare the permissions and features your app needs.

Request Permissions

To allow your app to use Android Beam file transfer to send files from external storage using NFC, you must request the following permissions in your app manifest:

NFC
Allows your app to send data over NFC. To specify this permission, add the following element as a child of the <manifest> element:
    <uses-permission android:name="android.permission.NFC" />
READ_EXTERNAL_STORAGE
Allows your app to read from external storage. To specify this permission, add the following element as a child of the <manifest> element:
    <uses-permission
            android:name="android.permission.READ_EXTERNAL_STORAGE" />

Note: As of Android 4.2.2 (API level 17), this permission is not enforced. Future versions of the platform may require it for apps that want to read from external storage. To ensure forward compatibility, request the permission now, before it becomes required.

Specify the NFC feature

Specify that your app uses NFC, by adding a <uses-feature> element as a child of the <manifest> element. Set the android:required attribute to true to indicate that your app won't function unless NFC is present.

The following snippet shows you how to specify the <uses-feature> element:

<uses-feature
    android:name="android.hardware.nfc"
    android:required="true" />

Note that if your app only uses NFC as an option, but still functions if NFC isn't present, you should set android:required to false, and test for NFC in code.

Specify Android Beam file transfer

Since Android Beam file transfer is only available in Android 4.1 (API level 16) and later, if your app depends on Android Beam file transfer for a key part of its functionality you must specify the <uses-sdk> element with the android:minSdkVersion="16" attribute. Otherwise, you can set android:minSdkVersion to another value as necessary, and test for the platform version in code, as described in the following section.

Test for Android Beam File Transfer Support

To specify in your app manifest that NFC is optional, you use the following element:

<uses-feature android:name="android.hardware.nfc" android:required="false" />

If you set the attribute android:required="false", you must test for NFC support and Android Beam file transfer support in code.

To test for Android Beam file transfer support in code, start by testing that the device supports NFC by calling PackageManager.hasSystemFeature() with the argument FEATURE_NFC. Next, check that the Android version supports Android Beam file transfer by testing the value of SDK_INT. If Android Beam file transfer is supported, get an instance of the NFC controller, which allows you to communicate with the NFC hardware. For example:

public class MainActivity extends Activity {
    ...
    NfcAdapter mNfcAdapter;
    // Flag to indicate that Android Beam is available
    boolean mAndroidBeamAvailable  = false;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // NFC isn't available on the device
        if (!PackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
            /*
             * Disable NFC features here.
             * For example, disable menu items or buttons that activate
             * NFC-related features
             */
            ...
        // Android Beam file transfer isn't supported
        } else if (Build.VERSION.SDK_INT <
                Build.VERSION_CODES.JELLY_BEAN_MR1) {
            // If Android Beam isn't available, don't continue.
            mAndroidBeamAvailable = false;
            /*
             * Disable Android Beam file transfer features here.
             */
            ...
        // Android Beam file transfer is available, continue
        } else {
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        ...
        }
    }
    ...
}

Create a Callback Method that Provides Files

Once you've verified that the device supports Android Beam file transfer, add a callback method that the system invokes when Android Beam file transfer detects that the user wants to send files to another NFC-enabled device. In this callback method, return an array of Uri objects. Android Beam file transfer copies the files represented by these URIs to the receiving device.

To add the callback method, implement the NfcAdapter.CreateBeamUrisCallback interface and its method createBeamUris(). The following snippet shows you how to do this:

public class MainActivity extends Activity {
    ...
    // List of URIs to provide to Android Beam
    private Uri[] mFileUris = new Uri[10];
    ...
    /**
     * Callback that Android Beam file transfer calls to get
     * files to share
     */
    private class FileUriCallback implements
            NfcAdapter.CreateBeamUrisCallback {
        public FileUriCallback() {
        }
        /**
         * Create content URIs as needed to share with another device
         */
        @Override
        public Uri[] createBeamUris(NfcEvent event) {
            return mFileUris;
        }
    }
    ...
}

Once you've implemented the interface, provide the callback to Android Beam file transfer by calling setBeamPushUrisCallback(). The following snippet shows you how to do this:

public class MainActivity extends Activity {
    ...
    // Instance that returns available files from this app
    private FileUriCallback mFileUriCallback;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Android Beam file transfer is available, continue
        ...
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        /*
         * Instantiate a new FileUriCallback to handle requests for
         * URIs
         */
        mFileUriCallback = new FileUriCallback();
        // Set the dynamic callback for URI requests.
        mNfcAdapter.setBeamPushUrisCallback(mFileUriCallback,this);
        ...
    }
    ...
}

Note: You can also provide the array of Uri objects directly to the NFC framework through your app's NfcAdapter instance. Choose this approach if you can define the URIs to transfer before the NFC touch event occurs. To learn more about this approach, see NfcAdapter.setBeamPushUris().

Specify the Files to Send

To transfer one or more files to another NFC-enabled device, get a file URI (a URI with a file scheme) for each file and then add the URI to an array of Uri objects. To transfer a file, you must also have permanent read access for the file. For example, the following snippet shows you how to get a file URI from a file name and then add the URI to the array:

        /*
         * Create a list of URIs, get a File,
         * and set its permissions
         */
        private Uri[] mFileUris = new Uri[10];
        String transferFile = "transferimage.jpg";
        File extDir = getExternalFilesDir(null);
        File requestFile = new File(extDir, transferFile);
        requestFile.setReadable(true, false);
        // Get a URI for the File and add it to the list of URIs
        fileUri = Uri.fromFile(requestFile);
        if (fileUri != null) {
            mFileUris[0] = fileUri;
        } else {
            Log.e("My Activity", "No File URI available for file.");
        }
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Receiving Files from Another Device | Android Developers Skip to content

Most visited

Recently visited

navigation

Receiving Files from Another Device

Android Beam file transfer copies files to a special directory on the receiving device. It also scans the copied files using the Android Media Scanner and adds entries for media files to the MediaStore provider. This lesson shows you how to respond when the file copy is complete, and how to locate the copied files on the receiving device.

Respond to a Request to Display Data

When Android Beam file transfer finishes copying files to the receiving device, it posts a notification containing an Intent with the action ACTION_VIEW, the MIME type of the first file that was transferred, and a URI that points to the first file. When the user clicks the notification, this intent is sent out to the system. To have your app respond to this intent, add an <intent-filter> element for the <activity> element of the Activity that should respond. In the <intent-filter> element, add the following child elements:

<action android:name="android.intent.action.VIEW" />
Matches the ACTION_VIEW intent sent from the notification.
<category android:name="android.intent.category.CATEGORY_DEFAULT" />
Matches an Intent that doesn't have an explicit category.
<data android:mimeType="mime-type" />
Matches a MIME type. Specify only those MIME types that your app can handle.

For example, the following snippet shows you how to add an intent filter that triggers the activity com.example.android.nfctransfer.ViewActivity:

    <activity
        android:name="com.example.android.nfctransfer.ViewActivity"
        android:label="Android Beam Viewer" >
        ...
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            ...
        </intent-filter>
    </activity>

Note: Android Beam file transfer is not the only source of an ACTION_VIEW intent. Other apps on the receiving device can also send an Intent with this action. Handling this situation is discussed in the section Get the directory from a content URI.

Request File Permissions

To read files that Android Beam file transfer copies to the device, request the permission READ_EXTERNAL_STORAGE. For example:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

If you want to copy transferred files to your app's own storage area, request the permission WRITE_EXTERNAL_STORAGE instead. WRITE_EXTERNAL_STORAGE includes READ_EXTERNAL_STORAGE.

Note: As of Android 4.2.2 (API level 17), the permission READ_EXTERNAL_STORAGE is only enforced if the user chooses to do so. Future versions of the platform may require this permission in all cases. To ensure forward compatibility, request the permission now, before it becomes required.

Since your app has control over its internal storage area, you don't need to request write permission to copy a transferred file to your internal storage area.

Get the Directory for Copied Files

Android Beam file transfer copies all the files in a single transfer to one directory on the receiving device. The URI in the content Intent sent by the Android Beam file transfer notification points to the first transferred file. However, your app may also receive an ACTION_VIEW intent from a source other than Android Beam file transfer. To determine how you should handle the incoming Intent, you need to examine its scheme and authority.

To get the scheme for the URI, call Uri.getScheme(). The following code snippet shows you how to determine the scheme and handle the URI accordingly:

public class MainActivity extends Activity {
    ...
    // A File object containing the path to the transferred files
    private File mParentPath;
    // Incoming Intent
    private Intent mIntent;
    ...
    /*
     * Called from onNewIntent() for a SINGLE_TOP Activity
     * or onCreate() for a new Activity. For onNewIntent(),
     * remember to call setIntent() to store the most
     * current Intent
     *
     */
    private void handleViewIntent() {
        ...
        // Get the Intent action
        mIntent = getIntent();
        String action = mIntent.getAction();
        /*
         * For ACTION_VIEW, the Activity is being asked to display data.
         * Get the URI.
         */
        if (TextUtils.equals(action, Intent.ACTION_VIEW)) {
            // Get the URI from the Intent
            Uri beamUri = mIntent.getData();
            /*
             * Test for the type of URI, by getting its scheme value
             */
            if (TextUtils.equals(beamUri.getScheme(), "file")) {
                mParentPath = handleFileUri(beamUri);
            } else if (TextUtils.equals(
                    beamUri.getScheme(), "content")) {
                mParentPath = handleContentUri(beamUri);
            }
        }
        ...
    }
    ...
}

Get the directory from a file URI

If the incoming Intent contains a file URI, the URI contains the absolute file name of a file, including the full directory path and file name. For Android Beam file transfer, the directory path points to the location of the other transferred files, if any. To get the directory path, get the path part of the URI, which contains all of the URI except the file: prefix. Create a File from the path part, then get the parent path of the File:

    ...
    public String handleFileUri(Uri beamUri) {
        // Get the path part of the URI
        String fileName = beamUri.getPath();
        // Create a File object for this filename
        File copiedFile = new File(fileName);
        // Get a string containing the file's parent directory
        return copiedFile.getParent();
    }
    ...

Get the directory from a content URI

If the incoming Intent contains a content URI, the URI may point to a directory and file name stored in the MediaStore content provider. You can detect a content URI for MediaStore by testing the URI's authority value. A content URI for MediaStore may come from Android Beam file transfer or from another app, but in both cases you can retrieve a directory and file name for the content URI.

You can also receive an incoming ACTION_VIEW intent containing a content URI for a content provider other than MediaStore. In this case, the content URI doesn't contain the MediaStore authority value, and the content URI usually doesn't point to a directory.

Note: For Android Beam file transfer, you receive a content URI in the ACTION_VIEW intent if the first incoming file has a MIME type of "audio/*", "image/*", or "video/*", indicating that the file is media- related. Android Beam file transfer indexes the media files it transfers by running Media Scanner on the directory where it stores transferred files. Media Scanner writes its results to the MediaStore content provider, then it passes a content URI for the first file back to Android Beam file transfer. This content URI is the one you receive in the notification Intent. To get the directory of the first file, you retrieve it from MediaStore using the content URI.

Determine the content provider

To determine if you can retrieve a file directory from the content URI, determine the the content provider associated with the URI by calling Uri.getAuthority() to get the URI's authority. The result has two possible values:

MediaStore.AUTHORITY
The URI is for a file or files tracked by MediaStore. Retrieve the full file name from MediaStore, and get directory from the file name.
Any other authority value
A content URI from another content provider. Display the data associated with the content URI, but don't get the file directory.

To get the directory for a MediaStore content URI, run a query that specifies the incoming content URI for the Uri argument and the column MediaColumns.DATA for the projection. The returned Cursor contains the full path and name for the file represented by the URI. This path also contains all the other files that Android Beam file transfer just copied to the device.

The following snippet shows you how to test the authority of the content URI and retrieve the the path and file name for the transferred file:

    ...
    public String handleContentUri(Uri beamUri) {
        // Position of the filename in the query Cursor
        int filenameIndex;
        // File object for the filename
        File copiedFile;
        // The filename stored in MediaStore
        String fileName;
        // Test the authority of the URI
        if (!TextUtils.equals(beamUri.getAuthority(), MediaStore.AUTHORITY)) {
            /*
             * Handle content URIs for other content providers
             */
        // For a MediaStore content URI
        } else {
            // Get the column that contains the file name
            String[] projection = { MediaStore.MediaColumns.DATA };
            Cursor pathCursor =
                    getContentResolver().query(beamUri, projection,
                    null, null, null);
            // Check for a valid cursor
            if (pathCursor != null &&
                    pathCursor.moveToFirst()) {
                // Get the column index in the Cursor
                filenameIndex = pathCursor.getColumnIndex(
                        MediaStore.MediaColumns.DATA);
                // Get the full file name including path
                fileName = pathCursor.getString(filenameIndex);
                // Create a File object for the filename
                copiedFile = new File(fileName);
                // Return the parent directory of the file
                return new File(copiedFile.getParent());
             } else {
                // The query didn't work; return null
                return null;
             }
        }
    }
    ...

To learn more about retrieving data from a content provider, see the section Retrieving Data from the Provider.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Building Apps with Multimedia | Android Developers Skip to content

Most visited

Recently visited

navigation

Building Apps with Multimedia

These classes teach you how to create rich multimedia apps that behave the way users expect.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Managing Audio Playback | Android Developers Skip to content

Most visited

Recently visited

navigation

Managing Audio Playback

Dependencies and prerequisites

You should also read

If your app plays audio, it’s important that your users can control the audio in a predictable manner. To ensure a great user experience, it’s also important that your app manages the audio focus to ensure multiple apps aren’t playing audio at the same time.

After this class, you will be able to build apps that respond to hardware audio key presses, which request audio focus when playing audio, and which respond appropriately to changes in audio focus caused by the system or other applications.

Lessons

Controlling Your App’s Volume and Playback
Learn how to ensure your users can control the volume of your app using the hardware or software volume controls and where available the play, stop, pause, skip, and previous media playback keys.
Managing Audio Focus
With multiple apps potentially playing audio it's important to think about how they should interact. To avoid every music app playing at the same time, Android uses audio focus to moderate audio playback. Learn how to request the audio focus, listen for a loss of audio focus, and how to respond when that happens.
Dealing with Audio Output Hardware
Audio can be played from a number of sources. Learn how to find out where the audio is being played and how to handle a headset being disconnected during playback.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Controlling Your App’s Volume and Playback | Android Developers Skip to content

Most visited

Recently visited

navigation

Controlling Your App’s Volume and Playback

A good user experience is a predictable one. If your app plays media it’s important that your users can control the volume of your app using the hardware or software volume controls of their device, bluetooth headset, or headphones.

Similarly, where appropriate and available, the play, stop, pause, skip, and previous media playback keys should perform their respective actions on the audio stream used by your app.

Identify Which Audio Stream to Use

The first step to creating a predictable audio experience is understanding which audio stream your app will use.

Android maintains a separate audio stream for playing music, alarms, notifications, the incoming call ringer, system sounds, in-call volume, and DTMF tones. This is done primarily to allow users to control the volume of each stream independently.

Most of these streams are restricted to system events, so unless your app is a replacement alarm clock, you’ll almost certainly be playing your audio using the STREAM_MUSIC stream.

Use Hardware Volume Keys to Control Your App’s Audio Volume

By default, pressing the volume controls modify the volume of the active audio stream. If your app isn't currently playing anything, hitting the volume keys adjusts the ringer volume.

If you've got a game or music app, then chances are good that when the user hits the volume keys they want to control the volume of the game or music, even if they’re currently between songs or there’s no music in the current game location.

You may be tempted to try and listen for volume key presses and modify the volume of your audio stream that way. Resist the urge. Android provides the handy setVolumeControlStream() method to direct volume key presses to the audio stream you specify.

Having identified the audio stream your application will be using, you should set it as the volume stream target. You should make this call early in your app’s lifecycle—because you only need to call it once during the activity lifecycle, you should typically call it within the onCreate() method (of the Activity or Fragment that controls your media). This ensures that whenever your app is visible, the volume controls function as the user expects.

setVolumeControlStream(AudioManager.STREAM_MUSIC);

From this point onwards, pressing the volume keys on the device affect the audio stream you specify (in this case “music”) whenever the target activity or fragment is visible.

Use Hardware Playback Control Keys to Control Your App’s Audio Playback

Media playback buttons such as play, pause, stop, skip, and previous are available on some handsets and many connected or wireless headsets. Whenever a user presses one of these hardware keys, the system broadcasts an intent with the ACTION_MEDIA_BUTTON action.

To respond to media button clicks, you need to register a BroadcastReceiver in your manifest that listens for this action broadcast as shown below.

<receiver android:name=".RemoteControlReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

The receiver implementation itself needs to extract which key was pressed to cause the broadcast. The Intent includes this under the EXTRA_KEY_EVENT key, while the KeyEvent class includes a list KEYCODE_MEDIA_* static constants that represents each of the possible media buttons, such as KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_MEDIA_NEXT.

The following snippet shows how to extract the media button pressed and affects the media playback accordingly.

public class RemoteControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
            KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
                // Handle key press.
            }
        }
    }
}

Because multiple applications might want to listen for media button presses, you must also programmatically control when your app should receive media button press events.

The following code can be used within your app to register and de-register your media button event receiver using the AudioManager. When registered, your broadcast receiver is the exclusive receiver of all media button broadcasts.

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...

// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...

// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);

Typically, apps should unregister most of their receivers whenever they become inactive or invisible (such as during the onStop() callback). However, it’s not that simple for media playback apps—in fact, responding to media playback buttons is most important when your application isn’t visible and therefore can’t be controlled by the on-screen UI.

A better approach is to register and unregister the media button event receiver when your application gains and loses the audio focus. This is covered in detail in the next lesson.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Managing Audio Focus | Android Developers Skip to content

Most visited

Recently visited

navigation

Managing Audio Focus

This lesson teaches you to

  1. Request the Audio Focus
  2. Handle the Loss of Audio Focus
  3. Duck!

You should also read

With multiple apps potentially playing audio it's important to think about how they should interact. To avoid every music app playing at the same time, Android uses audio focus to moderate audio playback—only apps that hold the audio focus should play audio.

Before your app starts playing audio it should request—and receive—the audio focus. Likewise, it should know how to listen for a loss of audio focus and respond appropriately when that happens.

Request the Audio Focus

Before your app starts playing any audio, it should hold the audio focus for the stream it will be using. This is done with a call to requestAudioFocus() which returns AUDIOFOCUS_REQUEST_GRANTED if your request is successful.

You must specify which stream you're using and whether you expect to require transient or permanent audio focus. Request transient focus when you expect to play audio for only a short time (for example when playing navigation instructions). Request permanent audio focus when you plan to play audio for the foreseeable future (for example, when playing music).

The following snippet requests permanent audio focus on the music audio stream. You should request the audio focus immediately before you begin playback, such as when the user presses play or the background music for the next game level begins.

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...

// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
                                 // Use the music stream.
                                 AudioManager.STREAM_MUSIC,
                                 // Request permanent focus.
                                 AudioManager.AUDIOFOCUS_GAIN);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    am.registerMediaButtonEventReceiver(RemoteControlReceiver);
    // Start playback.
}

Once you've finished playback be sure to call abandonAudioFocus(). This notifies the system that you no longer require focus and unregisters the associated AudioManager.OnAudioFocusChangeListener. In the case of abandoning transient focus, this allows any interupted app to continue playback.

// Abandon audio focus when playback complete
am.abandonAudioFocus(afChangeListener);

When requesting transient audio focus you have an additional option: whether or not you want to enable "ducking." Normally, when a well-behaved audio app loses audio focus it immediately silences its playback. By requesting a transient audio focus that allows ducking you tell other audio apps that it’s acceptable for them to keep playing, provided they lower their volume until the focus returns to them.

// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
                             // Use the music stream.
                             AudioManager.STREAM_MUSIC,
                             // Request permanent focus.
                             AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback.
}

Ducking is particularly suitable for apps that use the audio stream intermittently, such as for audible driving directions.

Whenever another app requests audio focus as described above, its choice between permanent and transient (with or without support for ducking) audio focus is received by the listener you registered when requesting focus.

Handle the Loss of Audio Focus

If your app can request audio focus, it follows that it will in turn lose that focus when another app requests it. How your app responds to a loss of audio focus depends on the manner of that loss.

The onAudioFocusChange() callback method of the audio focus change listener you registered when requesting audio focus receives a parameter that describes the focus change event. Specifically, the possible focus loss events mirror the focus request types from the previous section—permanent loss, transient loss, and transient with ducking permitted.

Generally speaking, a transient (temporary) loss of audio focus should result in your app silencing it’s audio stream, but otherwise maintaining the same state. You should continue to monitor changes in audio focus and be prepared to resume playback where it was paused once you’ve regained the focus.

If the audio focus loss is permanent, it’s assumed that another application is now being used to listen to audio and your app should effectively end itself. In practical terms, that means stopping playback, removing media button listeners—allowing the new audio player to exclusively handle those events—and abandoning your audio focus. At that point, you would expect a user action (pressing play in your app) to be required before you resume playing audio.

In the following code snippet, we pause the playback or our media player object if the audio loss is transient and resume it when we have regained the focus. If the loss is permanent, it unregisters our media button event receiver and stops monitoring audio focus changes.

AudioManager.OnAudioFocusChangeListener afChangeListener =
    new AudioManager.OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {
                // Pause playback
            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
                // Resume playback
            } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
                am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
                am.abandonAudioFocus(afChangeListener);
                // Stop playback
            }
        }
    };

In the case of a transient loss of audio focus where ducking is permitted, rather than pausing playback, you can "duck" instead.

Duck!

Ducking is the process of lowering your audio stream output volume to make transient audio from another app easier to hear without totally disrupting the audio from your own application.

In the following code snippet lowers the volume on our media player object when we temporarily lose focus, then returns it to its previous level when we regain focus.

OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
            // Lower the volume
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Raise it back to normal
        }
    }
};

A loss of audio focus is the most important broadcast to react to, but not the only one. The system broadcasts a number of intents to alert you to changes in user’s audio experience. The next lesson demonstrates how to monitor them to improve the user’s overall experience.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Dealing with Audio Output Hardware | Android Developers Skip to content

Most visited

Recently visited

navigation

Dealing with Audio Output Hardware

Users have a number of alternatives when it comes to enjoying the audio from their Android devices. Most devices have a built-in speaker, headphone jacks for wired headsets, and many also feature Bluetooth connectivity and support for A2DP audio.

Check What Hardware is Being Used

How your app behaves might be affected by which hardware its output is being routed to.

You can query the AudioManager to determine if the audio is currently being routed to the device speaker, wired headset, or attached Bluetooth device as shown in the following snippet:

if (isBluetoothA2dpOn()) {
    // Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
    // Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
    // Adjust output for headsets
} else { 
    // If audio plays and noone can hear it, is it still playing?
}

Handle Changes in the Audio Output Hardware

When a headset is unplugged, or a Bluetooth device disconnected, the audio stream automatically reroutes to the built in speaker. If you listen to your music at as high a volume as I do, that can be a noisy surprise.

Luckily the system broadcasts an ACTION_AUDIO_BECOMING_NOISY intent when this happens. It’s good practice to register a BroadcastReceiver that listens for this intent whenever you’re playing audio. In the case of music players, users typically expect the playback to be paused—while for games you may choose to significantly lower the volume.

private class NoisyAudioStreamReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
            // Pause the playback
        }
    }
}

private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

private void startPlayback() {
    registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
}

private void stopPlayback() {
    unregisterReceiver(myNoisyAudioStreamReceiver);
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Capturing Photos | Android Developers Skip to content

Most visited

Recently visited

navigation

Capturing Photos

Dependencies and prerequisites

  • Android 2.2 (API level 8) or higher
  • A device with a camera

You should also read

Try it out

Download the sample

PhotoIntentActivity.zip

The world was a dismal and featureless place before rich media became prevalent. Remember Gopher? We don't, either. For your app to become part of your users' lives, give them a way to put their lives into it. Using the on-board cameras, your application can enable users to augment what they see around them, make unique avatars, look for zombies around the corner, or simply share their experiences.

This class gets you clicking fast with some super-easy ways of leveraging existing camera applications. In later lessons, you dive deeper and learn how to control the camera hardware directly.

Lessons

Taking Photos Simply
Leverage other applications and capture photos with just a few lines of code.
Recording Videos Simply
Leverage other applications and record videos with just a few lines of code.
Controlling the Camera
Control the camera hardware directly and implement your own camera application.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Taking Photos Simply | Android Developers Skip to content

Most visited

Recently visited

navigation

Taking Photos Simply

This lesson explains how to capture photos using an existing camera application.

Suppose you are implementing a crowd-sourced weather service that makes a global weather map by blending together pictures of the sky taken by devices running your client app. Integrating photos is only a small part of your application. You want to take photos with minimal fuss, not reinvent the camera. Happily, most Android-powered devices already have at least one camera application installed. In this lesson, you learn how to make it take a picture for you.

Request Camera Permission

If an essential function of your application is taking pictures, then restrict its visibility on Google Play to devices that have a camera. To advertise that your application depends on having a camera, put a <uses-feature> tag in your manifest file:

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
    ...
</manifest>

If your application uses, but does not require a camera in order to function, instead set android:required to false. In doing so, Google Play will allow devices without a camera to download your application. It's then your responsibility to check for the availability of the camera at runtime by calling hasSystemFeature(PackageManager.FEATURE_CAMERA). If a camera is not available, you should then disable your camera features.

Take a Photo with the Camera App

The Android way of delegating actions to other applications is to invoke an Intent that describes what you want done. This process involves three pieces: The Intent itself, a call to start the external Activity, and some code to handle the image data when focus returns to your activity.

Here's a function that invokes an intent to capture a photo.

static final int REQUEST_IMAGE_CAPTURE = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }
}

Notice that the startActivityForResult() method is protected by a condition that calls resolveActivity(), which returns the first activity component that can handle the intent. Performing this check is important because if you call startActivityForResult() using an intent that no app can handle, your app will crash. So as long as the result is not null, it's safe to use the intent.

Get the Thumbnail

If the simple feat of taking a photo is not the culmination of your app's ambition, then you probably want to get the image back from the camera application and do something with it.

The Android Camera application encodes the photo in the return Intent delivered to onActivityResult() as a small Bitmap in the extras, under the key "data". The following code retrieves this image and displays it in an ImageView.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extras.get("data");
        mImageView.setImageBitmap(imageBitmap);
    }
}

Note: This thumbnail image from "data" might be good for an icon, but not a lot more. Dealing with a full-sized image takes a bit more work.

Save the Full-size Photo

The Android Camera application saves a full-size photo if you give it a file to save into. You must provide a fully qualified file name where the camera app should save the photo.

Generally, any photos that the user captures with the device camera should be saved on the device in the public external storage so they are accessible by all apps. The proper directory for shared photos is provided by getExternalStoragePublicDirectory(), with the DIRECTORY_PICTURES argument. Because the directory provided by this method is shared among all apps, reading and writing to it requires the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions, respectively. The write permission implicitly allows reading, so if you need to write to the external storage then you need to request only one permission:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

However, if you'd like the photos to remain private to your app only, you can instead use the directory provided by getExternalFilesDir(). On Android 4.3 and lower, writing to this directory also requires the WRITE_EXTERNAL_STORAGE permission. Beginning with Android 4.4, the permission is no longer required because the directory is not accessible by other apps, so you can declare the permission should be requested only on the lower versions of Android by adding the maxSdkVersion attribute:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...
</manifest>

Note: Files you save in the directories provided by getExternalFilesDir() or getFilesDir() are deleted when the user uninstalls your app.

Once you decide the directory for the file, you need to create a collision-resistant file name. You may wish also to save the path in a member variable for later use. Here's an example solution in a method that returns a unique file name for a new photo using a date-time stamp:

String mCurrentPhotoPath;

private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
        imageFileName,  /* prefix */
        ".jpg",         /* suffix */
        storageDir      /* directory */
    );

    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = "file:" + image.getAbsolutePath();
    return image;
}

With this method available to create a file for the photo, you can now create and invoke the Intent like this:

static final int REQUEST_TAKE_PHOTO = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // Ensure that there's a camera activity to handle the intent
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        // Create the File where the photo should go
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            // Error occurred while creating the File
            ...
        }
        // Continue only if the File was successfully created
        if (photoFile != null) {
            Uri photoURI = FileProvider.getUriForFile(this,
                                                  "com.example.android.fileprovider",
                                                  photoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
        }
    }
}

Note: We are using getUriForFile(Context, String, File) which returns a content:// URI. For more recent apps targeting Android N and higher, passing a file:// URI across a package boundary causes a FileUriExposedException. Therefore, we now present a more generic way of storing images using a FileProvider.

Now, you need to configure the FileProvider. In your app's manifest, add a provider to your application:
<application>
   ...
   <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.android.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"></meta-data>
    </provider>
    ...
</application>
Make sure that the authorities string matches the second argument to getUriForFile(Context, String, File). In the meta-data section of the provider definition, you can see that the provider expects eligible paths to be configured in a dedicated resource file, res/xml/file_paths.xml. Here is the content required for this particular example:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>
The path component corresponds to the path that is returned by getExternalFilesDir() when called with Environment.DIRECTORY_PICTURES. Make sure that you replace com.example.package.name with the actual package name of your app. Also, checkout the documentation of FileProvider for an extensive description of path specifiers that you can use besides external-path.

Add the Photo to a Gallery

When you create a photo through an intent, you should know where your image is located, because you said where to save it in the first place. For everyone else, perhaps the easiest way to make your photo accessible is to make it accessible from the system's Media Provider.

Note: If you saved your photo to the directory provided by getExternalFilesDir(), the media scanner cannot access the files because they are private to your app.

The following example method demonstrates how to invoke the system's media scanner to add your photo to the Media Provider's database, making it available in the Android Gallery application and to other apps.

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

Decode a Scaled Image

Managing multiple full-sized images can be tricky with limited memory. If you find your application running out of memory after displaying just a few images, you can dramatically reduce the amount of dynamic heap used by expanding the JPEG into a memory array that's already scaled to match the size of the destination view. The following example method demonstrates this technique.

private void setPic() {
    // Get the dimensions of the View
    int targetW = mImageView.getWidth();
    int targetH = mImageView.getHeight();

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    mImageView.setImageBitmap(bitmap);
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Recording Videos Simply | Android Developers Skip to content

Most visited

Recently visited

navigation

Recording Videos Simply

This lesson teaches you to

  1. Request Camera Permission
  2. Record a Video with a Camera App
  3. View the Video

You should also read

Try it out

Download the sample

PhotoIntentActivity.zip

This lesson explains how to capture video using existing camera applications.

Your application has a job to do, and integrating videos is only a small part of it. You want to take videos with minimal fuss, and not reinvent the camcorder. Happily, most Android-powered devices already have a camera application that records video. In this lesson, you make it do this for you.

Request Camera Permission

To advertise that your application depends on having a camera, put a <uses-feature> tag in the manifest file:

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
    ...
</manifest>

If your application uses, but does not require a camera in order to function, set android:required to false. In doing so, Google Play will allow devices without a camera to download your application. It's then your responsibility to check for the availability of the camera at runtime by calling hasSystemFeature(PackageManager.FEATURE_CAMERA). If a camera is not available, you should then disable your camera features.

Record a Video with a Camera App

The Android way of delegating actions to other applications is to invoke an Intent that describes what you want done. This process involves three pieces: The Intent itself, a call to start the external Activity, and some code to handle the video when focus returns to your activity.

Here's a function that invokes an intent to capture video.

static final int REQUEST_VIDEO_CAPTURE = 1;

private void dispatchTakeVideoIntent() {
    Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);
    }
}

Notice that the startActivityForResult() method is protected by a condition that calls resolveActivity(), which returns the first activity component that can handle the intent. Performing this check is important because if you call startActivityForResult() using an intent that no app can handle, your app will crash. So as long as the result is not null, it's safe to use the intent.

View the Video

The Android Camera application returns the video in the Intent delivered to onActivityResult() as a Uri pointing to the video location in storage. The following code retrieves this video and displays it in a VideoView.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
        Uri videoUri = intent.getData();
        mVideoView.setVideoURI(videoUri);
    }
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Controlling the Camera | Android Developers Skip to content

Most visited

Recently visited

navigation

Controlling the Camera

In this lesson, we discuss how to control the camera hardware directly using the framework APIs.

Directly controlling a device camera requires a lot more code than requesting pictures or videos from existing camera applications. However, if you want to build a specialized camera application or something fully integrated in your app UI, this lesson shows you how.

Open the Camera Object

Getting an instance of the Camera object is the first step in the process of directly controlling the camera. As Android's own Camera application does, the recommended way to access the camera is to open Camera on a separate thread that's launched from onCreate(). This approach is a good idea since it can take a while and might bog down the UI thread. In a more basic implementation, opening the camera can be deferred to the onResume() method to facilitate code reuse and keep the flow of control simple.

Calling Camera.open() throws an exception if the camera is already in use by another application, so we wrap it in a try block.

private boolean safeCameraOpen(int id) {
    boolean qOpened = false;
  
    try {
        releaseCameraAndPreview();
        mCamera = Camera.open(id);
        qOpened = (mCamera != null);
    } catch (Exception e) {
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    }

    return qOpened;    
}

private void releaseCameraAndPreview() {
    mPreview.setCamera(null);
    if (mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}

Since API level 9, the camera framework supports multiple cameras. If you use the legacy API and call open() without an argument, you get the first rear-facing camera.

Create the Camera Preview

Taking a picture usually requires that your users see a preview of their subject before clicking the shutter. To do so, you can use a SurfaceView to draw previews of what the camera sensor is picking up.

Preview Class

To get started with displaying a preview, you need preview class. The preview requires an implementation of the android.view.SurfaceHolder.Callback interface, which is used to pass image data from the camera hardware to the application.

class Preview extends ViewGroup implements SurfaceHolder.Callback {

    SurfaceView mSurfaceView;
    SurfaceHolder mHolder;

    Preview(Context context) {
        super(context);

        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
...
}

The preview class must be passed to the Camera object before the live image preview can be started, as shown in the next section.

Set and Start the Preview

A camera instance and its related preview must be created in a specific order, with the camera object being first. In the snippet below, the process of initializing the camera is encapsulated so that Camera.startPreview() is called by the setCamera() method, whenever the user does something to change the camera. The preview must also be restarted in the preview class surfaceChanged() callback method.

public void setCamera(Camera camera) {
    if (mCamera == camera) { return; }
    
    stopPreviewAndFreeCamera();
    
    mCamera = camera;
    
    if (mCamera != null) {
        List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
        mSupportedPreviewSizes = localSizes;
        requestLayout();
      
        try {
            mCamera.setPreviewDisplay(mHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
      
        // Important: Call startPreview() to start updating the preview
        // surface. Preview must be started before you can take a picture.
        mCamera.startPreview();
    }
}

Modify Camera Settings

Camera settings change the way that the camera takes pictures, from the zoom level to exposure compensation. This example changes only the preview size; see the source code of the Camera application for many more.

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Now that the size is known, set up the camera parameters and begin
    // the preview.
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    requestLayout();
    mCamera.setParameters(parameters);

    // Important: Call startPreview() to start updating the preview surface.
    // Preview must be started before you can take a picture.
    mCamera.startPreview();
}

Set the Preview Orientation

Most camera applications lock the display into landscape mode because that is the natural orientation of the camera sensor. This setting does not prevent you from taking portrait-mode photos, because the orientation of the device is recorded in the EXIF header. The setCameraDisplayOrientation() method lets you change how the preview is displayed without affecting how the image is recorded. However, in Android prior to API level 14, you must stop your preview before changing the orientation and then restart it.

Take a Picture

Use the Camera.takePicture() method to take a picture once the preview is started. You can create Camera.PictureCallback and Camera.ShutterCallback objects and pass them into Camera.takePicture().

If you want to grab images continously, you can create a Camera.PreviewCallback that implements onPreviewFrame(). For something in between, you can capture only selected preview frames, or set up a delayed action to call takePicture().

Restart the Preview

After a picture is taken, you must restart the preview before the user can take another picture. In this example, the restart is done by overloading the shutter button.

@Override
public void onClick(View v) {
    switch(mPreviewState) {
    case K_STATE_FROZEN:
        mCamera.startPreview();
        mPreviewState = K_STATE_PREVIEW;
        break;

    default:
        mCamera.takePicture( null, rawCallback, null);
        mPreviewState = K_STATE_BUSY;
    } // switch
    shutterBtnConfig();
}

Stop the Preview and Release the Camera

Once your application is done using the camera, it's time to clean up. In particular, you must release the Camera object, or you risk crashing other applications, including new instances of your own application.

When should you stop the preview and release the camera? Well, having your preview surface destroyed is a pretty good hint that it’s time to stop the preview and release the camera, as shown in these methods from the Preview class.

public void surfaceDestroyed(SurfaceHolder holder) {
    // Surface will be destroyed when we return, so stop the preview.
    if (mCamera != null) {
        // Call stopPreview() to stop updating the preview surface.
        mCamera.stopPreview();
    }
}

/**
 * When this function returns, mCamera will be null.
 */
private void stopPreviewAndFreeCamera() {

    if (mCamera != null) {
        // Call stopPreview() to stop updating the preview surface.
        mCamera.stopPreview();
    
        // Important: Call release() to release the camera for use by other
        // applications. Applications should release the camera immediately
        // during onPause() and re-open() it during onResume()).
        mCamera.release();
    
        mCamera = null;
    }
}

Earlier in the lesson, this procedure was also part of the setCamera() method, so initializing a camera always begins with stopping the preview.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Printing Content | Android Developers Skip to content

Most visited

Recently visited

navigation

Printing Content

Dependencies and prerequisites

  • Android 4.4 (API Level 19) or higher

Video

DevBytes: Android 4.4 Printing API

Android users frequently view content solely on their devices, but there are times when showing someone a screen is not an adequate way to share information. Being able to print information from your Android application gives users a way to see a larger version of the content from your app or share it with another person who is not using your application. Printing also allows them to create a snapshot of information that does not depend on having a device, sufficient battery power, or a wireless network connection.

In Android 4.4 (API level 19) and higher, the framework provides services for printing images and documents directly from Android applications. This training describes how to enable printing in your application, including printing images, HTML pages and creating custom documents for printing.

Lessons

Printing a Photo
This lesson shows you how to print an image.
Printing an HTML Document
This lesson shows you how to print an HTML document.
Printing a Custom Document
This lesson shows you how you connect to the Android print manager, create a print adapter and build content for printing.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Building Apps with Graphics & Animation | Android Developers Skip to content

Most visited

Recently visited

navigation

Building Apps with Graphics & Animation

These classes teach you how to accomplish tasks with graphics that can give your app an edge on the competition. If you want to go beyond the basic user interface to create a beautiful visual experience, these classes will help you get there.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Displaying Bitmaps Efficiently | Android Developers Skip to content

Most visited

Recently visited

navigation

Displaying Bitmaps Efficiently

Dependencies and prerequisites

Try it out

Download the sample

DisplayingBitmaps.zip

Video

DevBytes: Bitmap Allocation

Video

DevBytes: Making Apps Beautiful - Part 4 - Performance Tuning

Learn how to use common techniques to process and load Bitmap objects in a way that keeps your user interface (UI) components responsive and avoids exceeding your application memory limit. If you're not careful, bitmaps can quickly consume your available memory budget leading to an application crash due to the dreaded exception:
java.lang.OutofMemoryError: bitmap size exceeds VM budget.

There are a number of reasons why loading bitmaps in your Android application is tricky:

  • Mobile devices typically have constrained system resources. Android devices can have as little as 16MB of memory available to a single application. The Android Compatibility Definition Document (CDD), Section 3.7. Virtual Machine Compatibility gives the required minimum application memory for various screen sizes and densities. Applications should be optimized to perform under this minimum memory limit. However, keep in mind many devices are configured with higher limits.
  • Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the camera on the Galaxy Nexus takes photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is ARGB_8888 (the default from the Android 2.3 onward) then loading this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the per-app limit on some devices.
  • Android app UI’s frequently require several bitmaps to be loaded at once. Components such as ListView, GridView and ViewPager commonly include multiple bitmaps on-screen at once with many more potentially off-screen ready to show at the flick of a finger.

Lessons

Loading Large Bitmaps Efficiently
This lesson walks you through decoding large bitmaps without exceeding the per application memory limit.
Processing Bitmaps Off the UI Thread
Bitmap processing (resizing, downloading from a remote source, etc.) should never take place on the main UI thread. This lesson walks you through processing bitmaps in a background thread using AsyncTask and explains how to handle concurrency issues.
Caching Bitmaps
This lesson walks you through using a memory and disk bitmap cache to improve the responsiveness and fluidity of your UI when loading multiple bitmaps.
Managing Bitmap Memory
This lesson explains how to manage bitmap memory to maximize your app's performance.
Displaying Bitmaps in Your UI
This lesson brings everything together, showing you how to load multiple bitmaps into components like ViewPager and GridView using a background thread and bitmap cache.
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Loading Large Bitmaps Efficiently | Android Developers Skip to content

Most visited

Recently visited

navigation

Loading Large Bitmaps Efficiently

This lesson teaches you to

  1. Read Bitmap Dimensions and Type
  2. Load a Scaled Down Version into Memory

Try it out

Download the sample

DisplayingBitmaps.zip

Images come in all shapes and sizes. In many cases they are larger than required for a typical application user interface (UI). For example, the system Gallery application displays photos taken using your Android devices's camera which are typically much higher resolution than the screen density of your device.

Given that you are working with limited memory, ideally you only want to load a lower resolution version in memory. The lower resolution version should match the size of the UI component that displays it. An image with a higher resolution does not provide any visible benefit, but still takes up precious memory and incurs additional performance overhead due to additional on the fly scaling.

This lesson walks you through decoding large bitmaps without exceeding the per application memory limit by loading a smaller subsampled version in memory.

Read Bitmap Dimensions and Type

The BitmapFactory class provides several decoding methods (decodeByteArray(), decodeFile(), decodeResource(), etc.) for creating a Bitmap from various sources. Choose the most appropriate decode method based on your image data source. These methods attempt to allocate memory for the constructed bitmap and therefore can easily result in an OutOfMemory exception. Each type of decode method has additional signatures that let you specify decoding options via the BitmapFactory.Options class. Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidth, outHeight and outMimeType. This technique allows you to read the dimensions and type of the image data prior to construction (and memory allocation) of the bitmap.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it, unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.

Load a Scaled Down Version into Memory

Now that the image dimensions are known, they can be used to decide if the full image should be loaded into memory or if a subsampled version should be loaded instead. Here are some factors to consider:

  • Estimated memory usage of loading the full image in memory.
  • Amount of memory you are willing to commit to loading this image given any other memory requirements of your application.
  • Dimensions of the target ImageView or UI component that the image is to be loaded into.
  • Screen size and density of the current device.

For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.

To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize to true in your BitmapFactory.Options object. For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration of ARGB_8888). Here’s a method to calculate a sample size value that is a power of two based on a target width and height:

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

Note: A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the inSampleSize documentation.

To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSize value and inJustDecodeBounds set to false:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

This method makes it easy to load a bitmap of arbitrarily large size into an ImageView that displays a 100x100 pixel thumbnail, as shown in the following example code:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

You can follow a similar process to decode bitmaps from other sources, by substituting the appropriate BitmapFactory.decode* method as needed.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Processing Bitmaps Off the UI Thread | Android Developers Skip to content

Most visited

Recently visited

navigation

Processing Bitmaps Off the UI Thread

This lesson teaches you to

  1. Use an AsyncTask
  2. Handle Concurrency

You should also read

Try it out

Download the sample

DisplayingBitmaps.zip

The BitmapFactory.decode* methods, discussed in the Load Large Bitmaps Efficiently lesson, should not be executed on the main UI thread if the source data is read from disk or a network location (or really any source other than memory). The time this data takes to load is unpredictable and depends on a variety of factors (speed of reading from disk or network, size of image, power of CPU, etc.). If one of these tasks blocks the UI thread, the system flags your application as non-responsive and the user has the option of closing it (see Designing for Responsiveness for more information).

This lesson walks you through processing bitmaps in a background thread using AsyncTask and shows you how to handle concurrency issues.

Use an AsyncTask

The AsyncTask class provides an easy way to execute some work in a background thread and publish the results back on the UI thread. To use it, create a subclass and override the provided methods. Here’s an example of loading a large image into an ImageView using AsyncTask and decodeSampledBitmapFromResource():

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;
    private int data = 0;

    public BitmapWorkerTask(ImageView imageView) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        data = params[0];
        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
    }

    // Once complete, see if ImageView is still around and set bitmap.
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

The WeakReference to the ImageView ensures that the AsyncTask does not prevent the ImageView and anything it references from being garbage collected. There’s no guarantee the ImageView is still around when the task finishes, so you must also check the reference in onPostExecute(). The ImageView may no longer exist, if for example, the user navigates away from the activity or if a configuration change happens before the task finishes.

To start loading the bitmap asynchronously, simply create a new task and execute it:

public void loadBitmap(int resId, ImageView imageView) {
    BitmapWorkerTask task = new BitmapWorkerTask(imageView);
    task.execute(resId);
}

Handle Concurrency

Common view components such as ListView and GridView introduce another issue when used in conjunction with the AsyncTask as demonstrated in the previous section. In order to be efficient with memory, these components recycle child views as the user scrolls. If each child view triggers an AsyncTask, there is no guarantee that when it completes, the associated view has not already been recycled for use in another child view. Furthermore, there is no guarantee that the order in which asynchronous tasks are started is the order that they complete.

The blog post Multithreading for Performance further discusses dealing with concurrency, and offers a solution where the ImageView stores a reference to the most recent AsyncTask which can later be checked when the task completes. Using a similar method, the AsyncTask from the previous section can be extended to follow a similar pattern.

Create a dedicated Drawable subclass to store a reference back to the worker task. In this case, a BitmapDrawable is used so that a placeholder image can be displayed in the ImageView while the task completes:

static class AsyncDrawable extends BitmapDrawable {
    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

    public AsyncDrawable(Resources res, Bitmap bitmap,
            BitmapWorkerTask bitmapWorkerTask) {
        super(res, bitmap);
        bitmapWorkerTaskReference =
            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
    }

    public BitmapWorkerTask getBitmapWorkerTask() {
        return bitmapWorkerTaskReference.get();
    }
}

Before executing the BitmapWorkerTask, you create an AsyncDrawable and bind it to the target ImageView:

public void loadBitmap(int resId, ImageView imageView) {
    if (cancelPotentialWork(resId, imageView)) {
        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
        final AsyncDrawable asyncDrawable =
                new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
        imageView.setImageDrawable(asyncDrawable);
        task.execute(resId);
    }
}

The cancelPotentialWork method referenced in the code sample above checks if another running task is already associated with the ImageView. If so, it attempts to cancel the previous task by calling cancel(). In a small number of cases, the new task data matches the existing task and nothing further needs to happen. Here is the implementation of cancelPotentialWork:

public static boolean cancelPotentialWork(int data, ImageView imageView) {
    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

    if (bitmapWorkerTask != null) {
        final int bitmapData = bitmapWorkerTask.data;
        // If bitmapData is not yet set or it differs from the new data
        if (bitmapData == 0 || bitmapData != data) {
            // Cancel previous task
            bitmapWorkerTask.cancel(true);
        } else {
            // The same work is already in progress
            return false;
        }
    }
    // No task associated with the ImageView, or an existing task was cancelled
    return true;
}

A helper method, getBitmapWorkerTask(), is used above to retrieve the task associated with a particular ImageView:

private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
   if (imageView != null) {
       final Drawable drawable = imageView.getDrawable();
       if (drawable instanceof AsyncDrawable) {
           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
           return asyncDrawable.getBitmapWorkerTask();
       }
    }
    return null;
}

The last step is updating onPostExecute() in BitmapWorkerTask so that it checks if the task is cancelled and if the current task matches the one associated with the ImageView:

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            final BitmapWorkerTask bitmapWorkerTask =
                    getBitmapWorkerTask(imageView);
            if (this == bitmapWorkerTask && imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

This implementation is now suitable for use in ListView and GridView components as well as any other components that recycle their child views. Simply call loadBitmap where you normally set an image to your ImageView. For example, in a GridView implementation this would be in the getView() method of the backing adapter.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Caching Bitmaps | Android Developers Skip to content

Most visited

Recently visited

navigation

Caching Bitmaps

This lesson teaches you to

  1. Use a Memory Cache
  2. Use a Disk Cache
  3. Handle Configuration Changes

You should also read

Try it out

Download the sample

DisplayingBitmaps.zip

Loading a single bitmap into your user interface (UI) is straightforward, however things get more complicated if you need to load a larger set of images at once. In many cases (such as with components like ListView, GridView or ViewPager), the total number of images on-screen combined with images that might soon scroll onto the screen are essentially unlimited.

Memory usage is kept down with components like this by recycling the child views as they move off-screen. The garbage collector also frees up your loaded bitmaps, assuming you don't keep any long lived references. This is all good and well, but in order to keep a fluid and fast-loading UI you want to avoid continually processing these images each time they come back on-screen. A memory and disk cache can often help here, allowing components to quickly reload processed images.

This lesson walks you through using a memory and disk bitmap cache to improve the responsiveness and fluidity of your UI when loading multiple bitmaps.

Use a Memory Cache

A memory cache offers fast access to bitmaps at the cost of taking up valuable application memory. The LruCache class (also available in the Support Library for use back to API Level 4) is particularly well suited to the task of caching bitmaps, keeping recently referenced objects in a strong referenced LinkedHashMap and evicting the least recently used member before the cache exceeds its designated size.

Note: In the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap cache, however this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive with collecting soft/weak references which makes them fairly ineffective. In addition, prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.

In order to choose a suitable size for a LruCache, a number of factors should be taken into consideration, for example:

  • How memory intensive is the rest of your activity and/or application?
  • How many images will be on-screen at once? How many need to be available ready to come on-screen?
  • What is the screen size and density of the device? An extra high density screen (xhdpi) device like Galaxy Nexus will need a larger cache to hold the same number of images in memory compared to a device like Nexus S (hdpi).
  • What dimensions and configuration are the bitmaps and therefore how much memory will each take up?
  • How frequently will the images be accessed? Will some be accessed more frequently than others? If so, perhaps you may want to keep certain items always in memory or even have multiple LruCache objects for different groups of bitmaps.
  • Can you balance quality against quantity? Sometimes it can be more useful to store a larger number of lower quality bitmaps, potentially loading a higher quality version in another background task.

There is no specific size or formula that suits all applications, it's up to you to analyze your usage and come up with a suitable solution. A cache that is too small causes additional overhead with no benefit, a cache that is too large can once again cause java.lang.OutOfMemory exceptions and leave the rest of your app little memory to work with.

Here’s an example of setting up a LruCache for bitmaps:

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get max available VM memory, exceeding this amount will throw an
    // OutOfMemory exception. Stored in kilobytes as LruCache takes an
    // int in its constructor.
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    // Use 1/8th of the available memory for this memory cache.
    final int cacheSize = maxMemory / 8;

    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in kilobytes rather than
            // number of items.
            return bitmap.getByteCount() / 1024;
        }
    };
    ...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}

Note: In this example, one eighth of the application memory is allocated for our cache. On a normal/hdpi device this is a minimum of around 4MB (32/8). A full screen GridView filled with images on a device with 800x480 resolution would use around 1.5MB (800*480*4 bytes), so this would cache a minimum of around 2.5 pages of images in memory.

When loading a bitmap into an ImageView, the LruCache is checked first. If an entry is found, it is used immediately to update the ImageView, otherwise a background thread is spawned to process the image:

public void loadBitmap(int resId, ImageView imageView) {
    final String imageKey = String.valueOf(resId);

    final Bitmap bitmap = getBitmapFromMemCache(imageKey);
    if (bitmap != null) {
        mImageView.setImageBitmap(bitmap);
    } else {
        mImageView.setImageResource(R.drawable.image_placeholder);
        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
        task.execute(resId);
    }
}

The BitmapWorkerTask also needs to be updated to add entries to the memory cache:

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final Bitmap bitmap = decodeSampledBitmapFromResource(
                getResources(), params[0], 100, 100));
        addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
        return bitmap;
    }
    ...
}

Use a Disk Cache

A memory cache is useful in speeding up access to recently viewed bitmaps, however you cannot rely on images being available in this cache. Components like GridView with larger datasets can easily fill up a memory cache. Your application could be interrupted by another task like a phone call, and while in the background it might be killed and the memory cache destroyed. Once the user resumes, your application has to process each image again.

A disk cache can be used in these cases to persist processed bitmaps and help decrease loading times where images are no longer available in a memory cache. Of course, fetching images from disk is slower than loading from memory and should be done in a background thread, as disk read times can be unpredictable.

Note: A ContentProvider might be a more appropriate place to store cached images if they are accessed more frequently, for example in an image gallery application.

The sample code of this class uses a DiskLruCache implementation that is pulled from the Android source. Here’s updated example code that adds a disk cache in addition to the existing memory cache:

private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Initialize memory cache
    ...
    // Initialize disk cache on background thread
    File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
    new InitDiskCacheTask().execute(cacheDir);
    ...
}

class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
    @Override
    protected Void doInBackground(File... params) {
        synchronized (mDiskCacheLock) {
            File cacheDir = params[0];
            mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
            mDiskCacheStarting = false; // Finished initialization
            mDiskCacheLock.notifyAll(); // Wake any waiting threads
        }
        return null;
    }
}

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final String imageKey = String.valueOf(params[0]);

        // Check disk cache in background thread
        Bitmap bitmap = getBitmapFromDiskCache(imageKey);

        if (bitmap == null) { // Not found in disk cache
            // Process as normal
            final Bitmap bitmap = decodeSampledBitmapFromResource(
                    getResources(), params[0], 100, 100));
        }

        // Add final bitmap to caches
        addBitmapToCache(imageKey, bitmap);

        return bitmap;
    }
    ...
}

public void addBitmapToCache(String key, Bitmap bitmap) {
    // Add to memory cache as before
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }

    // Also add to disk cache
    synchronized (mDiskCacheLock) {
        if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
            mDiskLruCache.put(key, bitmap);
        }
    }
}

public Bitmap getBitmapFromDiskCache(String key) {
    synchronized (mDiskCacheLock) {
        // Wait while disk cache is started from background thread
        while (mDiskCacheStarting) {
            try {
                mDiskCacheLock.wait();
            } catch (InterruptedException e) {}
        }
        if (mDiskLruCache != null) {
            return mDiskLruCache.get(key);
        }
    }
    return null;
}

// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getDiskCacheDir(Context context, String uniqueName) {
    // Check if media is mounted or storage is built-in, if so, try and use external cache dir
    // otherwise use internal cache dir
    final String cachePath =
            Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                    !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
                            context.getCacheDir().getPath();

    return new File(cachePath + File.separator + uniqueName);
}

Note: Even initializing the disk cache requires disk operations and therefore should not take place on the main thread. However, this does mean there's a chance the cache is accessed before initialization. To address this, in the above implementation, a lock object ensures that the app does not read from the disk cache until the cache has been initialized.

While the memory cache is checked in the UI thread, the disk cache is checked in the background thread. Disk operations should never take place on the UI thread. When image processing is complete, the final bitmap is added to both the memory and disk cache for future use.

Handle Configuration Changes

Runtime configuration changes, such as a screen orientation change, cause Android to destroy and restart the running activity with the new configuration (For more information about this behavior, see Handling Runtime Changes). You want to avoid having to process all your images again so the user has a smooth and fast experience when a configuration change occurs.

Luckily, you have a nice memory cache of bitmaps that you built in the Use a Memory Cache section. This cache can be passed through to the new activity instance using a Fragment which is preserved by calling setRetainInstance(true)). After the activity has been recreated, this retained Fragment is reattached and you gain access to the existing cache object, allowing images to be quickly fetched and re-populated into the ImageView objects.

Here’s an example of retaining a LruCache object across configuration changes using a Fragment:

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    RetainFragment retainFragment =
            RetainFragment.findOrCreateRetainFragment(getFragmentManager());
    mMemoryCache = retainFragment.mRetainedCache;
    if (mMemoryCache == null) {
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            ... // Initialize cache here as usual
        }
        retainFragment.mRetainedCache = mMemoryCache;
    }
    ...
}

class RetainFragment extends Fragment {
    private static final String TAG = "RetainFragment";
    public LruCache<String, Bitmap> mRetainedCache;

    public RetainFragment() {}

    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
        RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
        if (fragment == null) {
            fragment = new RetainFragment();
            fm.beginTransaction().add(fragment, TAG).commit();
        }
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }
}

To test this out, try rotating a device both with and without retaining the Fragment. You should notice little to no lag as the images populate the activity almost instantly from memory when you retain the cache. Any images not found in the memory cache are hopefully available in the disk cache, if not, they are processed as usual.

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Managing Bitmap Memory | Android Developers Skip to content

Most visited

Recently visited

navigation

Managing Bitmap Memory

In addition to the steps described in Caching Bitmaps, there are specific things you can do to facilitate garbage collection and bitmap reuse. The recommended strategy depends on which version(s) of Android you are targeting. The BitmapFun sample app included with this class shows you how to design your app to work efficiently across different versions of Android.

To set the stage for this lesson, here is how Android's management of bitmap memory has evolved:

  • On Android Android 2.2 (API level 8) and lower, when garbage collection occurs, your app's threads get stopped. This causes a lag that can degrade performance. Android 2.3 adds concurrent garbage collection, which means that the memory is reclaimed soon after a bitmap is no longer referenced.
  • On Android 2.3.3 (API level 10) and lower, the backing pixel data for a bitmap is stored in native memory. It is separate from the bitmap itself, which is stored in the Dalvik heap. The pixel data in native memory is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash. As of Android 3.0 (API level 11), the pixel data is stored on the Dalvik heap along with the associated bitmap.

The following sections describe how to optimize bitmap memory management for different Android versions.

Manage Memory on Android 2.3.3 and Lower

On Android 2.3.3 (API level 10) and lower, using recycle() is recommended. If you're displaying large amounts of bitmap data in your app, you're likely to run into OutOfMemoryError errors. The recycle() method allows an app to reclaim memory as soon as possible.

Caution: You should use recycle() only when you are sure that the bitmap is no longer being used. If you call recycle() and later attempt to draw the bitmap, you will get the error: "Canvas: trying to use a recycled bitmap".

The following code snippet gives an example of calling recycle(). It uses reference counting (in the variables mDisplayRefCount and mCacheRefCount) to track whether a bitmap is currently being displayed or in the cache. The code recycles the bitmap when these conditions are met:

  • The reference count for both mDisplayRefCount and mCacheRefCount is 0.
  • The bitmap is not null, and it hasn't been recycled yet.
private int mCacheRefCount = 0;
private int mDisplayRefCount = 0;
...
// Notify the drawable that the displayed state has changed.
// Keep a count to determine when the drawable is no longer displayed.
public void setIsDisplayed(boolean isDisplayed) {
    synchronized (this) {
        if (isDisplayed) {
            mDisplayRefCount++;
            mHasBeenDisplayed = true;
        } else {
            mDisplayRefCount--;
        }
    }
    // Check to see if recycle() can be called.
    checkState();
}

// Notify the drawable that the cache state has changed.
// Keep a count to determine when the drawable is no longer being cached.
public void setIsCached(boolean isCached) {
    synchronized (this) {
        if (isCached) {
            mCacheRefCount++;
        } else {
            mCacheRefCount--;
        }
    }
    // Check to see if recycle() can be called.
    checkState();
}

private synchronized void checkState() {
    // If the drawable cache and display ref counts = 0, and this drawable
    // has been displayed, then recycle.
    if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
            && hasValidBitmap()) {
        getBitmap().recycle();
    }
}

private synchronized boolean hasValidBitmap() {
    Bitmap bitmap = getBitmap();
    return bitmap != null && !bitmap.isRecycled();
}

Manage Memory on Android 3.0 and Higher

Android 3.0 (API level 11) introduces the BitmapFactory.Options.inBitmap field. If this option is set, decode methods that take the Options object will attempt to reuse an existing bitmap when loading content. This means that the bitmap's memory is reused, resulting in improved performance, and removing both memory allocation and de-allocation. However, there are certain restrictions with how inBitmap can be used. In particular, before Android 4.4 (API level 19), only equal sized bitmaps are supported. For details, please see the inBitmap documentation.

Save a bitmap for later use

The following snippet demonstrates how an existing bitmap is stored for possible later use in the sample app. When an app is running on Android 3.0 or higher and a bitmap is evicted from the LruCache, a soft reference to the bitmap is placed in a HashSet, for possible reuse later with inBitmap:

Set<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String, BitmapDrawable> mMemoryCache;

// If you're running on Honeycomb or newer, create a
// synchronized HashSet of references to reusable bitmaps.
if (Utils.hasHoneycomb()) {
    mReusableBitmaps =
            Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
}

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {

    // Notify the removed entry that is no longer being cached.
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
            // The removed entry is a recycling drawable, so notify it
            // that it has been removed from the memory cache.
            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {
            // The removed entry is a standard BitmapDrawable.
            if (Utils.hasHoneycomb()) {
                // We're running on Honeycomb or later, so add the bitmap
                // to a SoftReference set for possible use with inBitmap later.
                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));
            }
        }
    }
....
}

Use an existing bitmap

In the running app, decoder methods check to see if there is an existing bitmap they can use. For example:

public static Bitmap decodeSampledBitmapFromFile(String filename,
        int reqWidth, int reqHeight, ImageCache cache) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    ...
    BitmapFactory.decodeFile(filename, options);
    ...

    // If we're running on Honeycomb or newer, try to use inBitmap.
    if (Utils.hasHoneycomb()) {
        addInBitmapOptions(options, cache);
    }
    ...
    return BitmapFactory.decodeFile(filename, options);
}
The next snippet shows the addInBitmapOptions() method that is called in the above snippet. It looks for an existing bitmap to set as the value for inBitmap. Note that this method only sets a value for inBitmap if it finds a suitable match (your code should never assume that a match will be found):

private static void addInBitmapOptions(BitmapFactory.Options options,
        ImageCache cache) {
    // inBitmap only works with mutable bitmaps, so force the decoder to
    // return mutable bitmaps.
    options.inMutable = true;

    if (cache != null) {
        // Try to find a bitmap to use for inBitmap.
        Bitmap inBitmap = cache.getBitmapFromReusableSet(options);

        if (inBitmap != null) {
            // If a suitable bitmap has been found, set it as the value of
            // inBitmap.
            options.inBitmap = inBitmap;
        }
    }
}

// This method iterates through the reusable bitmaps, looking for one 
// to use for inBitmap:
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
        Bitmap bitmap = null;

    if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
        synchronized (mReusableBitmaps) {
            final Iterator<SoftReference<Bitmap>> iterator
                    = mReusableBitmaps.iterator();
            Bitmap item;

            while (iterator.hasNext()) {
                item = iterator.next().get();

                if (null != item && item.isMutable()) {
                    // Check to see it the item can be used for inBitmap.
                    if (canUseForInBitmap(item, options)) {
                        bitmap = item;

                        // Remove from reusable set so it can't be used again.
                        iterator.remove();
                        break;
                    }
                } else {
                    // Remove from the set if the reference has been cleared.
                    iterator.remove();
                }
            }
        }
    }
    return bitmap;
}

Finally, this method determines whether a candidate bitmap satisfies the size criteria to be used for inBitmap:

static boolean canUseForInBitmap(
        Bitmap candidate, BitmapFactory.Options targetOptions) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // From Android 4.4 (KitKat) onward we can re-use if the byte size of
        // the new bitmap is smaller than the reusable bitmap candidate
        // allocation byte count.
        int width = targetOptions.outWidth / targetOptions.inSampleSize;
        int height = targetOptions.outHeight / targetOptions.inSampleSize;
        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
        return byteCount <= candidate.getAllocationByteCount();
    }

    // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
    return candidate.getWidth() == targetOptions.outWidth
            && candidate.getHeight() == targetOptions.outHeight
            && targetOptions.inSampleSize == 1;
}

/**
 * A helper function to return the byte usage per pixel of a bitmap based on its configuration.
 */
static int getBytesPerPixel(Config config) {
    if (config == Config.ARGB_8888) {
        return 4;
    } else if (config == Config.RGB_565) {
        return 2;
    } else if (config == Config.ARGB_4444) {
        return 2;
    } else if (config == Config.ALPHA_8) {
        return 1;
    }
    return 1;
}
This site uses cookies to store your preferences for site-specific language and display options.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Displaying Bitmaps in Your UI | Android Developers Skip to content

Most visited

Recently visited

navigation

    Displaying Bitmaps in Your UI

    This lesson brings together everything from previous lessons, showing you how to load multiple bitmaps into ViewPager and GridView components using a background thread and bitmap cache, while dealing with concurrency and configuration changes.

    Load Bitmaps into a ViewPager Implementation

    The swipe view pattern is an excellent way to navigate the detail view of an image gallery. You can implement this pattern using a ViewPager component backed by a PagerAdapter. However, a more suitable backing adapter is the subclass FragmentStatePagerAdapter which automatically destroys and saves state of the Fragments in the ViewPager as they disappear off-screen, keeping memory usage down.

    Note: If you have a smaller number of images and are confident they all fit within the application memory limit, then using a regular PagerAdapter or FragmentPagerAdapter might be more appropriate.

    Here’s an implementation of a ViewPager with ImageView children. The main activity holds the ViewPager and the adapter:

    public class ImageDetailActivity extends FragmentActivity {
        public static final String EXTRA_IMAGE = "extra_image";
    
        private ImagePagerAdapter mAdapter;
        private ViewPager mPager;
    
        // A static dataset to back the ViewPager adapter
        public final static Integer[] imageResIds = new Integer[] {
                R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
                R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
                R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.image_detail_pager); // Contains just a ViewPager
    
            mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);
            mPager = (ViewPager) findViewById(R.id.pager);
            mPager.setAdapter(mAdapter);
        }
    
        public static class ImagePagerAdapter extends FragmentStatePagerAdapter {
            private final int mSize;
    
            public ImagePagerAdapter(FragmentManager fm, int size) {
                super(fm);
                mSize = size;
            }
    
            @Override
            public int getCount() {
                return mSize;
            }
    
            @Override
            public Fragment getItem(int position) {
                return ImageDetailFragment.newInstance(position);
            }
        }
    }
    

    Here is an implementation of the details Fragment which holds the ImageView children. This might seem like a perfectly reasonable approach, but can you see the drawbacks of this implementation? How could it be improved?

    public class ImageDetailFragment extends Fragment {
        private static final String IMAGE_DATA_EXTRA = "resId";
        private int mImageNum;
        private ImageView mImageView;
    
        static ImageDetailFragment newInstance(int imageNum) {
            final ImageDetailFragment f = new ImageDetailFragment();
            final Bundle args = new Bundle();
            args.putInt(IMAGE_DATA_EXTRA, imageNum);
            f.setArguments(args);
            return f;
        }
    
        // Empty constructor, required as per Fragment docs
        public ImageDetailFragment() {}
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            // image_detail_fragment.xml contains just an ImageView
            final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
            mImageView = (ImageView) v.findViewById(R.id.imageView);
            return v;
        }
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            final int resId = ImageDetailActivity.imageResIds[mImageNum];
            mImageView.setImageResource(resId); // Load image into ImageView
        }
    }
    

    Hopefully you noticed the issue: the images are being read from resources on the UI thread, which can lead to an application hanging and being force closed. Using an AsyncTask as described in the Processing Bitmaps Off the UI Thread lesson, it’s straightforward to move image loading and processing to a background thread:

    public class ImageDetailActivity extends FragmentActivity {
        ...
    
        public void loadBitmap(int resId, ImageView imageView) {
            mImageView.setImageResource(R.drawable.image_placeholder);
            BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
            task.execute(resId);
        }
    
        ... // include BitmapWorkerTask class
    }
    
    public class ImageDetailFragment extends Fragment {
        ...
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            if (ImageDetailActivity.class.isInstance(getActivity())) {
                final int resId = ImageDetailActivity.imageResIds[mImageNum];
                // Call out to ImageDetailActivity to load the bitmap in a background thread
                ((ImageDetailActivity) getActivity()).loadBitmap(resId, mImageView);
            }
        }
    }
    

    Any additional processing (such as resizing or fetching images from the network) can take place in the BitmapWorkerTask without affecting responsiveness of the main UI. If the background thread is doing more than just loading an image directly from disk, it can also be beneficial to add a memory and/or disk cache as described in the lesson Caching Bitmaps. Here's the additional modifications for a memory cache:

    public class ImageDetailActivity extends FragmentActivity {
        ...
        private LruCache<String, Bitmap> mMemoryCache;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            ...
            // initialize LruCache as per Use a Memory Cache section
        }
    
        public void loadBitmap(int resId, ImageView imageView) {
            final String imageKey = String.valueOf(resId);
    
            final Bitmap bitmap = mMemoryCache.get(imageKey);
            if (bitmap != null) {
                mImageView.setImageBitmap(bitmap);
            } else {
                mImageView.setImageResource(R.drawable.image_placeholder);
                BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
                task.execute(resId);
            }
        }
    
        ... // include updated BitmapWorkerTask from Use a Memory Cache section
    }
    

    Putting all these pieces together gives you a responsive ViewPager implementation with minimal image loading latency and the ability to do as much or as little background processing on your images as needed.

    Load Bitmaps into a GridView Implementation

    The grid list building block is useful for showing image data sets and can be implemented using a GridView component in which many images can be on-screen at any one time and many more need to be ready to appear if the user scrolls up or down. When implementing this type of control, you must ensure the UI remains fluid, memory usage remains under control and concurrency is handled correctly (due to the way GridView recycles its children views).

    To start with, here is a standard GridView implementation with ImageView children placed inside a Fragment. Again, this might seem like a perfectly reasonable approach, but what would make it better?

    public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
        private ImageAdapter mAdapter;
    
        // A static dataset to back the GridView adapter
        public final static Integer[] imageResIds = new Integer[] {
                R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
                R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
                R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
    
        // Empty constructor as per Fragment docs
        public ImageGridFragment() {}
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mAdapter = new ImageAdapter(getActivity());
        }
    
        @Override
        public View onCreateView(
                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
            final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
            mGridView.setAdapter(mAdapter);
            mGridView.setOnItemClickListener(this);
            return v;
        }
    
        @Override
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
            i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
            startActivity(i);
        }
    
        private class ImageAdapter extends BaseAdapter {
            private final Context mContext;
    
            public ImageAdapter(Context context) {
                super();
                mContext = context;
            }
    
            @Override
            public int getCount() {
                return imageResIds.length;
            }
    
            @Override
            public Object getItem(int position) {
                return imageResIds[position];
            }
    
            @Override
            public long getItemId(int position) {
                return position;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup container) {
                ImageView imageView;
                if (convertView == null) { // if it's not recycled, initialize some attributes
                    imageView = new ImageView(mContext);
                    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                    imageView.setLayoutParams(new GridView.LayoutParams(
                            LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
                } else {
                    imageView = (ImageView) convertView;
                }
                imageView.setImageResource(imageResIds[position]); // Load image into ImageView
                return imageView;
            }
        }
    }
    

    Once again, the problem with this implementation is that the image is being set in the UI thread. While this may work for small, simple images (due to system resource loading and caching), if any additional processing needs to be done, your UI grinds to a halt.

    The same asynchronous processing and caching methods from the previous section can be implemented here. However, you also need to wary of concurrency issues as the GridView recycles its children views. To handle this, use the techniques discussed in the Processing Bitmaps Off the UI Thread lesson. Here is the updated solution:

    public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
        ...
    
        private class ImageAdapter extends BaseAdapter {
            ...
    
            @Override
            public View getView(int position, View convertView, ViewGroup container) {
                ...
                loadBitmap(imageResIds[position], imageView)
                return imageView;
            }
        }
    
        public void loadBitmap(int resId, ImageView imageView) {
            if (cancelPotentialWork(resId, imageView)) {
                final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
                final AsyncDrawable asyncDrawable =
                        new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
                imageView.setImageDrawable(asyncDrawable);
                task.execute(resId);
            }
        }
    
        static class AsyncDrawable extends BitmapDrawable {
            private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
    
            public AsyncDrawable(Resources res, Bitmap bitmap,
                    BitmapWorkerTask bitmapWorkerTask) {
                super(res, bitmap);
                bitmapWorkerTaskReference =
                    new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
            }
    
            public BitmapWorkerTask getBitmapWorkerTask() {
                return bitmapWorkerTaskReference.get();
            }
        }
    
        public static boolean cancelPotentialWork(int data, ImageView imageView) {
            final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
    
            if (bitmapWorkerTask != null) {
                final int bitmapData = bitmapWorkerTask.data;
                if (bitmapData != data) {
                    // Cancel previous task
                    bitmapWorkerTask.cancel(true);
                } else {
                    // The same work is already in progress
                    return false;
                }
            }
            // No task associated with the ImageView, or an existing task was cancelled
            return true;
        }
    
        private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
           if (imageView != null) {
               final Drawable drawable = imageView.getDrawable();
               if (drawable instanceof AsyncDrawable) {
                   final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
                   return asyncDrawable.getBitmapWorkerTask();
               }
            }
            return null;
        }
    
        ... // include updated BitmapWorkerTask class
    

    Note: The same code can easily be adapted to work with ListView as well.

    This implementation allows for flexibility in how the images are processed and loaded without impeding the smoothness of the UI. In the background task you can load images from the network or resize large digital camera photos and the images appear as the tasks finish processing.

    For a full example of this and other concepts discussed in this lesson, please see the included sample application.

    This site uses cookies to store your preferences for site-specific language and display options.

    This class requires API level or higher

    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

    Displaying Graphics with OpenGL ES | Android Developers Skip to content

    Most visited

    Recently visited

    navigation

      Displaying Graphics with OpenGL ES

      Dependencies and prerequisites

      • Android 2.2 (API Level 8) or higher
      • Experience building an Android app

      You should also read

      Try it out

      Download the sample

      OpenGLES.zip

      The Android framework provides plenty of standard tools for creating attractive, functional graphical user interfaces. However, if you want more control of what your application draws on screen, or are venturing into three dimensional graphics, you need to use a different tool. The OpenGL ES APIs provided by the Android framework offers a set of tools for displaying high-end, animated graphics that are limited only by your imagination and can also benefit from the acceleration of graphics processing units (GPUs) provided on many Android devices.

      This class walks you through the basics of developing applications that use OpenGL, including setup, drawing objects, moving drawn elements and responding to touch input.

      The example code in this class uses the OpenGL ES 2.0 APIs, which is the recommended API version to use with current Android devices. For more information about versions of OpenGL ES, see the OpenGL developer guide.

      Note: Be careful not to mix OpenGL ES 1.x API calls with OpenGL ES 2.0 methods! The two APIs are not interchangeable and trying to use them together only results in frustration and sadness.

      Lessons

      Building an OpenGL ES Environment
      Learn how to set up an Android application to be able to draw OpenGL graphics.
      Defining Shapes
      Learn how to define shapes and why you need to know about faces and winding.
      Drawing Shapes
      Learn how to draw OpenGL shapes in your application.
      Applying Projection and Camera Views
      Learn how to use projection and camera views to get a new perspective on your drawn objects.
      Adding Motion
      Learn how to do basic movement and animation of drawn objects with OpenGL.
      Responding to Touch Events
      Learn how to do basic interaction with OpenGL graphics.
      This site uses cookies to store your preferences for site-specific language and display options.

      This class requires API level or higher

      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

      Building an OpenGL ES Environment | Android Developers Skip to content

      Most visited

      Recently visited

      navigation

        Building an OpenGL ES Environment

        In order to draw graphics with OpenGL ES in your Android application, you must create a view container for them. One of the more straight-forward ways to do this is to implement both a GLSurfaceView and a GLSurfaceView.Renderer. A GLSurfaceView is a view container for graphics drawn with OpenGL and GLSurfaceView.Renderer controls what is drawn within that view. For more information about these classes, see the OpenGL ES developer guide.

        GLSurfaceView is just one way to incorporate OpenGL ES graphics into your application. For a full-screen or near-full screen graphics view, it is a reasonable choice. Developers who want to incorporate OpenGL ES graphics in a small portion of their layouts should take a look at TextureView. For real, do-it-yourself developers, it is also possible to build up an OpenGL ES view using SurfaceView, but this requires writing quite a bit of additional code.

        This lesson explains how to complete a minimal implementation of GLSurfaceView and GLSurfaceView.Renderer in a simple application activity.

        Declare OpenGL ES Use in the Manifest

        In order for your application to use the OpenGL ES 2.0 API, you must add the following declaration to your manifest:

        <uses-feature android:glEsVersion="0x00020000" android:required="true" />
        

        If your application uses texture compression, you must also declare which compression formats your app supports, so that it is only installed on compatible devices.

        <supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
        <supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
        

        For more information about texture compression formats, see the OpenGL developer guide.

        Create an Activity for OpenGL ES Graphics

        Android applications that use OpenGL ES have activities just like any other application that has a user interface. The main difference from other applications is what you put in the layout for your activity. While in many applications you might use TextView, Button and ListView, in an app that uses OpenGL ES, you can also add a GLSurfaceView.

        The following code example shows a minimal implementation of an activity that uses a GLSurfaceView as its primary view:

        public class OpenGLES20Activity extends Activity {
        
            private GLSurfaceView mGLView;
        
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
        
                // Create a GLSurfaceView instance and set it
                // as the ContentView for this Activity.
                mGLView = new MyGLSurfaceView(this);
                setContentView(mGLView);
            }
        }
        

        Note: OpenGL ES 2.0 requires Android 2.2 (API Level 8) or higher, so make sure your Android project targets that API or higher.

        Build a GLSurfaceView Object

        A GLSurfaceView is a specialized view where you can draw OpenGL ES graphics. It does not do much by itself. The actual drawing of objects is controlled in the GLSurfaceView.Renderer that you set on this view. In fact, the code for this object is so thin, you may be tempted to skip extending it and just create an unmodified GLSurfaceView instance, but don’t do that. You need to extend this class in order to capture touch events, which is covered in the Responding to Touch Events lesson.

        The essential code for a GLSurfaceView is minimal, so for a quick implementation, it is common to just create an inner class in the activity that uses it:

        class MyGLSurfaceView extends GLSurfaceView {
        
            private final MyGLRenderer mRenderer;
        
            public MyGLSurfaceView(Context context){
                super(context);
        
                // Create an OpenGL ES 2.0 context
                setEGLContextClientVersion(2);
        
                mRenderer = new MyGLRenderer();
        
                // Set the Renderer for drawing on the GLSurfaceView
                setRenderer(mRenderer);
            }
        }
        

        One other optional addition to your GLSurfaceView implementation is to set the render mode to only draw the view when there is a change to your drawing data using the GLSurfaceView.RENDERMODE_WHEN_DIRTY setting:

        // Render the view only when there is a change in the drawing data
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
        

        This setting prevents the GLSurfaceView frame from being redrawn until you call requestRender(), which is more efficient for this sample app.

        Build a Renderer Class

        The implementation of the GLSurfaceView.Renderer class, or renderer, within an application that uses OpenGL ES is where things start to get interesting. This class controls what gets drawn on the GLSurfaceView with which it is associated. There are three methods in a renderer that are called by the Android system in order to figure out what and how to draw on a GLSurfaceView:

        • onSurfaceCreated() - Called once to set up the view's OpenGL ES environment.
        • onDrawFrame() - Called for each redraw of the view.
        • onSurfaceChanged() - Called if the geometry of the view changes, for example when the device's screen orientation changes.

        Here is a very basic implementation of an OpenGL ES renderer, that does nothing more than draw a black background in the GLSurfaceView:

        public class MyGLRenderer implements GLSurfaceView.Renderer {
        
            public void onSurfaceCreated(GL10 unused, EGLConfig config) {
                // Set the background frame color
                GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            }
        
            public void onDrawFrame(GL10 unused) {
                // Redraw background color
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            }
        
            public void onSurfaceChanged(GL10 unused, int width, int height) {
                GLES20.glViewport(0, 0, width, height);
            }
        }
        

        That’s all there is to it! The code examples above create a simple Android application that displays a black screen using OpenGL. While this code does not do anything very interesting, by creating these classes, you have laid the foundation you need to start drawing graphic elements with OpenGL.

        Note: You may wonder why these methods have a GL10 parameter, when you are using the OpengGL ES 2.0 APIs. These method signatures are simply reused for the 2.0 APIs to keep the Android framework code simpler.

        If you are familiar with the OpenGL ES APIs, you should now be able to set up a OpenGL ES environment in your app and start drawing graphics. However, if you need a bit more help getting started with OpenGL, head on to the next lessons for a few more hints.

        This site uses cookies to store your preferences for site-specific language and display options.

        This class requires API level or higher

        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

        Defining Shapes | Android Developers Skip to content

        Most visited

        Recently visited

        navigation

          Defining Shapes

          This lesson teaches you to

          1. Define a Triangle
          2. Define a Square

          You should also read

          Download the sample

          OpenGLES.zip

          Being able to define shapes to be drawn in the context of an OpenGL ES view is the first step in creating your high-end graphics masterpiece. Drawing with OpenGL ES can be a little tricky without knowing a few basic things about how OpenGL ES expects you to define graphic objects.

          This lesson explains the OpenGL ES coordinate system relative to an Android device screen, the basics of defining a shape, shape faces, as well as defining a triangle and a square.

          Define a Triangle

          OpenGL ES allows you to define drawn objects using coordinates in three-dimensional space. So, before you can draw a triangle, you must define its coordinates. In OpenGL, the typical way to do this is to define a vertex array of floating point numbers for the coordinates. For maximum efficiency, you write these coordinates into a ByteBuffer, that is passed into the OpenGL ES graphics pipeline for processing.

          public class Triangle {
          
              private FloatBuffer vertexBuffer;
          
              // number of coordinates per vertex in this array
              static final int COORDS_PER_VERTEX = 3;
              static float triangleCoords[] = {   // in counterclockwise order:
                       0.0f,  0.622008459f, 0.0f, // top
                      -0.5f, -0.311004243f, 0.0f, // bottom left
                       0.5f, -0.311004243f, 0.0f  // bottom right
              };
          
              // Set color with red, green, blue and alpha (opacity) values
              float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
          
              public Triangle() {
                  // initialize vertex byte buffer for shape coordinates
                  ByteBuffer bb = ByteBuffer.allocateDirect(
                          // (number of coordinate values * 4 bytes per float)
                          triangleCoords.length * 4);
                  // use the device hardware's native byte order
                  bb.order(ByteOrder.nativeOrder());
          
                  // create a floating point buffer from the ByteBuffer
                  vertexBuffer = bb.asFloatBuffer();
                  // add the coordinates to the FloatBuffer
                  vertexBuffer.put(triangleCoords);
                  // set the buffer to read the first coordinate
                  vertexBuffer.position(0);
              }
          }
          

          By default, OpenGL ES assumes a coordinate system where [0,0,0] (X,Y,Z) specifies the center of the GLSurfaceView frame, [1,1,0] is the top right corner of the frame and [-1,-1,0] is bottom left corner of the frame. For an illustration of this coordinate system, see the OpenGL ES developer guide.

          Note that the coordinates of this shape are defined in a counterclockwise order. The drawing order is important because it defines which side is the front face of the shape, which you typically want to have drawn, and the back face, which you can choose to not draw using the OpenGL ES cull face feature. For more information about faces and culling, see the OpenGL ES developer guide.

          Define a Square

          Defining triangles is pretty easy in OpenGL, but what if you want to get a just a little more complex? Say, a square? There are a number of ways to do this, but a typical path to drawing such a shape in OpenGL ES is to use two triangles drawn together:

          Figure 1. Drawing a square using two triangles.

          Again, you should define the vertices in a counterclockwise order for both triangles that represent this shape, and put the values in a ByteBuffer. In order to avoid defining the two coordinates shared by each triangle twice, use a drawing list to tell the OpenGL ES graphics pipeline how to draw these vertices. Here’s the code for this shape:

          public class Square {
          
              private FloatBuffer vertexBuffer;
              private ShortBuffer drawListBuffer;
          
              // number of coordinates per vertex in this array
              static final int COORDS_PER_VERTEX = 3;
              static float squareCoords[] = {
                      -0.5f,  0.5f, 0.0f,   // top left
                      -0.5f, -0.5f, 0.0f,   // bottom left
                       0.5f, -0.5f, 0.0f,   // bottom right
                       0.5f,  0.5f, 0.0f }; // top right
          
              private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
          
              public Square() {
                  // initialize vertex byte buffer for shape coordinates
                  ByteBuffer bb = ByteBuffer.allocateDirect(
                  // (# of coordinate values * 4 bytes per float)
                          squareCoords.length * 4);
                  bb.order(ByteOrder.nativeOrder());
                  vertexBuffer = bb.asFloatBuffer();
                  vertexBuffer.put(squareCoords);
                  vertexBuffer.position(0);
          
                  // initialize byte buffer for the draw list
                  ByteBuffer dlb = ByteBuffer.allocateDirect(
                  // (# of coordinate values * 2 bytes per short)
                          drawOrder.length * 2);
                  dlb.order(ByteOrder.nativeOrder());
                  drawListBuffer = dlb.asShortBuffer();
                  drawListBuffer.put(drawOrder);
                  drawListBuffer.position(0);
              }
          }
          

          This example gives you a peek at what it takes to create more complex shapes with OpenGL. In general, you use collections of triangles to draw objects. In the next lesson, you learn how to draw these shapes on screen.

          This site uses cookies to store your preferences for site-specific language and display options.

          This class requires API level or higher

          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

          Drawing Shapes | Android Developers Skip to content

          Most visited

          Recently visited

          navigation

            Drawing Shapes

            This lesson teaches you to

            1. Initialize Shapes
            2. Draw a Shape

            You should also read

            Download the sample

            OpenGLES.zip

            After you define shapes to be drawn with OpenGL, you probably want to draw them. Drawing shapes with the OpenGL ES 2.0 takes a bit more code than you might imagine, because the API provides a great deal of control over the graphics rendering pipeline.

            This lesson explains how to draw the shapes you defined in the previous lesson using the OpenGL ES 2.0 API.

            Initialize Shapes

            Before you do any drawing, you must initialize and load the shapes you plan to draw. Unless the structure (the original coordinates) of the shapes you use in your program change during the course of execution, you should initialize them in the onSurfaceCreated() method of your renderer for memory and processing efficiency.

            public class MyGLRenderer implements GLSurfaceView.Renderer {
            
                ...
                private Triangle mTriangle;
                private Square   mSquare;
            
                public void onSurfaceCreated(GL10 unused, EGLConfig config) {
                    ...
            
                    // initialize a triangle
                    mTriangle = new Triangle();
                    // initialize a square
                    mSquare = new Square();
                }
                ...
            }
            

            Draw a Shape

            Drawing a defined shape using OpenGL ES 2.0 requires a significant amount of code, because you must provide a lot of details to the graphics rendering pipeline. Specifically, you must define the following:

            • Vertex Shader - OpenGL ES graphics code for rendering the vertices of a shape.
            • Fragment Shader - OpenGL ES code for rendering the face of a shape with colors or textures.
            • Program - An OpenGL ES object that contains the shaders you want to use for drawing one or more shapes.

            You need at least one vertex shader to draw a shape and one fragment shader to color that shape. These shaders must be complied and then added to an OpenGL ES program, which is then used to draw the shape. Here is an example of how to define basic shaders you can use to draw a shape in the Triangle class:

            public class Triangle {
            
                private final String vertexShaderCode =
                    "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = vPosition;" +
                    "}";
            
                private final String fragmentShaderCode =
                    "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";
            
                ...
            }
            

            Shaders contain OpenGL Shading Language (GLSL) code that must be compiled prior to using it in the OpenGL ES environment. To compile this code, create a utility method in your renderer class:

            public static int loadShader(int type, String shaderCode){
            
                // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
                // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
                int shader = GLES20.glCreateShader(type);
            
                // add the source code to the shader and compile it
                GLES20.glShaderSource(shader, shaderCode);
                GLES20.glCompileShader(shader);
            
                return shader;
            }
            

            In order to draw your shape, you must compile the shader code, add them to a OpenGL ES program object and then link the program. Do this in your drawn object’s constructor, so it is only done once.

            Note: Compiling OpenGL ES shaders and linking programs is expensive in terms of CPU cycles and processing time, so you should avoid doing this more than once. If you do not know the content of your shaders at runtime, you should build your code such that they only get created once and then cached for later use.

            public class Triangle() {
                ...
            
                private final int mProgram;
            
                public Triangle() {
                    ...
            
                    int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                                                    vertexShaderCode);
                    int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                                                    fragmentShaderCode);
            
                    // create empty OpenGL ES Program
                    mProgram = GLES20.glCreateProgram();
            
                    // add the vertex shader to program
                    GLES20.glAttachShader(mProgram, vertexShader);
            
                    // add the fragment shader to program
                    GLES20.glAttachShader(mProgram, fragmentShader);
            
                    // creates OpenGL ES program executables
                    GLES20.glLinkProgram(mProgram);
                }
            }
            

            At this point, you are ready to add the actual calls that draw your shape. Drawing shapes with OpenGL ES requires that you specify several parameters to tell the rendering pipeline what you want to draw and how to draw it. Since drawing options can vary by shape, it's a good idea to have your shape classes contain their own drawing logic.

            Create a draw() method for drawing the shape. This code sets the position and color values to the shape’s vertex shader and fragment shader, and then executes the drawing function.

            private int mPositionHandle;
            private int mColorHandle;
            
            private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
            private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
            
            public void draw() {
                // Add program to OpenGL ES environment
                GLES20.glUseProgram(mProgram);
            
                // get handle to vertex shader's vPosition member
                mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
            
                // Enable a handle to the triangle vertices
                GLES20.glEnableVertexAttribArray(mPositionHandle);
            
                // Prepare the triangle coordinate data
                GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                             GLES20.GL_FLOAT, false,
                                             vertexStride, vertexBuffer);
            
                // get handle to fragment shader's vColor member
                mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
            
                // Set color for drawing the triangle
                GLES20.glUniform4fv(mColorHandle, 1, color, 0);
            
                // Draw the triangle
                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
            
                // Disable vertex array
                GLES20.glDisableVertexAttribArray(mPositionHandle);
            }
            

            Once you have all this code in place, drawing this object just requires a call to the draw() method from within your renderer’s onDrawFrame() method:

            public void onDrawFrame(GL10 unused) {
                ...
            
                mTriangle.draw();
            }
            

            When you run the application, it should look something like this:

            Figure 1. Triangle drawn without a projection or camera view.

            There are a few problems with this code example. First of all, it is not going to impress your friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen orientation of the device. The reason the shape is skewed is due to the fact that the object’s vertices have not been corrected for the proportions of the screen area where the GLSurfaceView is displayed. You can fix that problem using a projection and camera view in the next lesson.

            Lastly, the triangle is stationary, which is a bit boring. In the Adding Motion lesson, you make this shape rotate and make more interesting use of the OpenGL ES graphics pipeline.

            This site uses cookies to store your preferences for site-specific language and display options.

            This class requires API level or higher

            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

            Applying Projection and Camera Views | Android Developers Skip to content

            Most visited

            Recently visited

            navigation

              Applying Projection and Camera Views

              In the OpenGL ES environment, projection and camera views allow you to display drawn objects in a way that more closely resembles how you see physical objects with your eyes. This simulation of physical viewing is done with mathematical transformations of drawn object coordinates:

              • Projection - This transformation adjusts the coordinates of drawn objects based on the width and height of the GLSurfaceView where they are displayed. Without this calculation, objects drawn by OpenGL ES are skewed by the unequal proportions of the view window. A projection transformation typically only has to be calculated when the proportions of the OpenGL view are established or changed in the onSurfaceChanged() method of your renderer. For more information about OpenGL ES projections and coordinate mapping, see Mapping Coordinates for Drawn Objects.
              • Camera View - This transformation adjusts the coordinates of drawn objects based on a virtual camera position. It’s important to note that OpenGL ES does not define an actual camera object, but instead provides utility methods that simulate a camera by transforming the display of drawn objects. A camera view transformation might be calculated only once when you establish your GLSurfaceView, or might change dynamically based on user actions or your application’s function.

              This lesson describes how to create a projection and camera view and apply it to shapes drawn in your GLSurfaceView.

              Define a Projection

              The data for a projection transformation is calculated in the onSurfaceChanged() method of your GLSurfaceView.Renderer class. The following example code takes the height and width of the GLSurfaceView and uses it to populate a projection transformation Matrix using the Matrix.frustumM() method:

              // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
              private final float[] mMVPMatrix = new float[16];
              private final float[] mProjectionMatrix = new float[16];
              private final float[] mViewMatrix = new float[16];
              
              @Override
              public void onSurfaceChanged(GL10 unused, int width, int height) {
                  GLES20.glViewport(0, 0, width, height);
              
                  float ratio = (float) width / height;
              
                  // this projection matrix is applied to object coordinates
                  // in the onDrawFrame() method
                  Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
              }
              

              This code populates a projection matrix, mProjectionMatrix which you can then combine with a camera view transformation in the onDrawFrame() method, which is shown in the next section.

              Note: Just applying a projection transformation to your drawing objects typically results in a very empty display. In general, you must also apply a camera view transformation in order for anything to show up on screen.

              Define a Camera View

              Complete the process of transforming your drawn objects by adding a camera view transformation as part of the drawing process in your renderer. In the following example code, the camera view transformation is calculated using the Matrix.setLookAtM() method and then combined with the previously calculated projection matrix. The combined transformation matrices are then passed to the drawn shape.

              @Override
              public void onDrawFrame(GL10 unused) {
                  ...
                  // Set the camera position (View matrix)
                  Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
              
                  // Calculate the projection and view transformation
                  Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
              
                  // Draw shape
                  mTriangle.draw(mMVPMatrix);
              }
              

              Apply Projection and Camera Transformations

              In order to use the combined projection and camera view transformation matrix shown in the previews sections, first add a matrix variable to the vertex shader previously defined in the Triangle class:

              public class Triangle {
              
                  private final String vertexShaderCode =
                      // This matrix member variable provides a hook to manipulate
                      // the coordinates of the objects that use this vertex shader
                      "uniform mat4 uMVPMatrix;" +
                      "attribute vec4 vPosition;" +
                      "void main() {" +
                      // the matrix must be included as a modifier of gl_Position
                      // Note that the uMVPMatrix factor *must be first* in order
                      // for the matrix multiplication product to be correct.
                      "  gl_Position = uMVPMatrix * vPosition;" +
                      "}";
              
                  // Use to access and set the view transformation
                  private int mMVPMatrixHandle;
              
                  ...
              }
              

              Next, modify the draw() method of your graphic objects to accept the combined transformation matrix and apply it to the shape:

              public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix
                  ...
              
                  // get handle to shape's transformation matrix
                  mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
              
                  // Pass the projection and view transformation to the shader
                  GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
              
                  // Draw the triangle
                  GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
              
                  // Disable vertex array
                  GLES20.glDisableVertexAttribArray(mPositionHandle);
              }
              

              Once you have correctly calculated and applied the projection and camera view transformations, your graphic objects are drawn in correct proportions and should look like this:

              Figure 1. Triangle drawn with a projection and camera view applied.

              Now that you have an application that displays your shapes in correct proportions, it's time to add motion to your shapes.

              This site uses cookies to store your preferences for site-specific language and display options.

              This class requires API level or higher

              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

              Adding Motion | Android Developers Skip to content

              Most visited

              Recently visited

              navigation

                Adding Motion

                This lesson teaches you to

                1. Rotate a Shape
                2. Enable Continuous Rendering

                You should also read

                Download the sample

                OpenGLES.zip

                Drawing objects on screen is a pretty basic feature of OpenGL, but you can do this with other Android graphics framwork classes, including Canvas and Drawable objects. OpenGL ES provides additional capabilities for moving and transforming drawn objects in three dimensions or in other unique ways to create compelling user experiences.

                In this lesson, you take another step forward into using OpenGL ES by learning how to add motion to a shape with rotation.

                Rotate a Shape

                Rotating a drawing object with OpenGL ES 2.0 is relatively simple. In your renderer, create another transformation matrix (a rotation matrix) and then combine it with your projection and camera view transformation matrices:

                private float[] mRotationMatrix = new float[16];
                public void onDrawFrame(GL10 gl) {
                    float[] scratch = new float[16];
                
                    ...
                
                    // Create a rotation transformation for the triangle
                    long time = SystemClock.uptimeMillis() % 4000L;
                    float angle = 0.090f * ((int) time);
                    Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);
                
                    // Combine the rotation matrix with the projection and camera view
                    // Note that the mMVPMatrix factor *must be first* in order
                    // for the matrix multiplication product to be correct.
                    Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
                
                    // Draw triangle
                    mTriangle.draw(scratch);
                }
                

                If your triangle does not rotate after making these changes, make sure you have commented out the GLSurfaceView.RENDERMODE_WHEN_DIRTY setting, as described in the next section.

                Enable Continuous Rendering

                If you have diligently followed along with the example code in this class to this point, make sure you comment out the line that sets the render mode only draw when dirty, otherwise OpenGL rotates the shape only one increment and then waits for a call to requestRender() from the GLSurfaceView container:

                public MyGLSurfaceView(Context context) {
                    ...
                    // Render the view only when there is a change in the drawing data.
                    // To allow the triangle to rotate automatically, this line is commented out:
                    //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
                }
                

                Unless you have objects changing without any user interaction, it’s usually a good idea have this flag turned on. Be ready to uncomment this code, because the next lesson makes this call applicable once again.

                This site uses cookies to store your preferences for site-specific language and display options.

                This class requires API level or higher

                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                Responding to Touch Events | Android Developers Skip to content

                Most visited

                Recently visited

                navigation

                  Responding to Touch Events

                  This lesson teaches you to

                  1. Setup a Touch Listener
                  2. Expose the Rotation Angle
                  3. Apply Rotation

                  You should also read

                  Download the sample

                  OpenGLES.zip

                  Making objects move according to a preset program like the rotating triangle is useful for getting some attention, but what if you want to have users interact with your OpenGL ES graphics? The key to making your OpenGL ES application touch interactive is expanding your implementation of GLSurfaceView to override the onTouchEvent() to listen for touch events.

                  This lesson shows you how to listen for touch events to let users rotate an OpenGL ES object.

                  Setup a Touch Listener

                  In order to make your OpenGL ES application respond to touch events, you must implement the onTouchEvent() method in your GLSurfaceView class. The example implementation below shows how to listen for MotionEvent.ACTION_MOVE events and translate them to an angle of rotation for a shape.

                  private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
                  private float mPreviousX;
                  private float mPreviousY;
                  
                  @Override
                  public boolean onTouchEvent(MotionEvent e) {
                      // MotionEvent reports input details from the touch screen
                      // and other input controls. In this case, you are only
                      // interested in events where the touch position changed.
                  
                      float x = e.getX();
                      float y = e.getY();
                  
                      switch (e.getAction()) {
                          case MotionEvent.ACTION_MOVE:
                  
                              float dx = x - mPreviousX;
                              float dy = y - mPreviousY;
                  
                              // reverse direction of rotation above the mid-line
                              if (y > getHeight() / 2) {
                                dx = dx * -1 ;
                              }
                  
                              // reverse direction of rotation to left of the mid-line
                              if (x < getWidth() / 2) {
                                dy = dy * -1 ;
                              }
                  
                              mRenderer.setAngle(
                                      mRenderer.getAngle() +
                                      ((dx + dy) * TOUCH_SCALE_FACTOR));
                              requestRender();
                      }
                  
                      mPreviousX = x;
                      mPreviousY = y;
                      return true;
                  }
                  

                  Notice that after calculating the rotation angle, this method calls requestRender() to tell the renderer that it is time to render the frame. This approach is the most efficient in this example because the frame does not need to be redrawn unless there is a change in the rotation. However, it does not have any impact on efficiency unless you also request that the renderer only redraw when the data changes using the setRenderMode() method, so make sure this line is uncommented in the renderer:

                  public MyGLSurfaceView(Context context) {
                      ...
                      // Render the view only when there is a change in the drawing data
                      setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
                  }
                  

                  Expose the Rotation Angle

                  The example code above requires that you expose the rotation angle through your renderer by adding a public member. Since the renderer code is running on a separate thread from the main user interface thread of your application, you must declare this public variable as volatile. Here is the code to declare the variable and expose the getter and setter pair:

                  public class MyGLRenderer implements GLSurfaceView.Renderer {
                      ...
                  
                      public volatile float mAngle;
                  
                      public float getAngle() {
                          return mAngle;
                      }
                  
                      public void setAngle(float angle) {
                          mAngle = angle;
                      }
                  }
                  

                  Apply Rotation

                  To apply the rotation generated by touch input, comment out the code that generates an angle and add mAngle, which contains the touch input generated angle:

                  public void onDrawFrame(GL10 gl) {
                      ...
                      float[] scratch = new float[16];
                  
                      // Create a rotation for the triangle
                      // long time = SystemClock.uptimeMillis() % 4000L;
                      // float angle = 0.090f * ((int) time);
                      Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
                  
                      // Combine the rotation matrix with the projection and camera view
                      // Note that the mMVPMatrix factor *must be first* in order
                      // for the matrix multiplication product to be correct.
                      Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
                  
                      // Draw triangle
                      mTriangle.draw(scratch);
                  }
                  

                  When you have completed the steps described above, run the program and drag your finger over the screen to rotate the triangle:

                  Figure 1. Triangle being rotated with touch input (circle shows touch location).

                  This site uses cookies to store your preferences for site-specific language and display options.

                  This class requires API level or higher

                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                  Animating Views Using Scenes and Transitions | Android Developers Skip to content

                  Most visited

                  Recently visited

                  navigation

                    Animating Views Using Scenes and Transitions

                    Dependencies and Prerequisites

                    • Android 4.4.2 (API level 19) or higher

                    You should also read

                    Try it out

                    Video

                    DevBytes: Android 4.4 Transitions

                    The user interface of an activity often changes in response to user input and other events. For example, an activity that contains a form where users can type search queries can hide the form when the user submits it and show a list of search results in its place.

                    To provide visual continuity in these situations, you can animate changes between different view hierarchies in your user interface. These animations give users feedback on their actions and help them learn how your app works.

                    Android includes the transitions framework, which enables you to easily animate changes between two view hierarchies. The framework animates the views at runtime by changing some of their property values over time. The framework includes built-in animations for common effects and lets you create custom animations and transition lifecycle callbacks.

                    This class teaches you to use the built-in animations in the transitions framework to animate changes between view hierarchies. This class also covers how to create custom animations.

                    Note: For Android versions earlier than 4.4.2 (API level 19) but greater than or equal to Android 4.0 (API level 14), use the animateLayoutChanges attribute to animate layouts. To learn more, see Property Animation and Animating Layout Changes.

                    Lessons

                    The Transitions Framework
                    Learn the main features and components of the transitions framework.
                    Creating a Scene
                    Learn how to create a scene to store the state of a view hierarchy.
                    Applying a Transition
                    Learn how to apply a transition between two scenes of a view hierarchy.
                    Creating Custom Transitions
                    Learn how to create other animation effects not included in the transitions framework.
                    This site uses cookies to store your preferences for site-specific language and display options.

                    This class requires API level or higher

                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                    Adding Animations | Android Developers Skip to content

                    Most visited

                    Recently visited

                    navigation

                      Adding Animations

                      Dependencies and prerequisites

                      You should also read

                      Try it out

                      Download the sample app

                      Animations.zip

                      Animations can add subtle visual cues that notify users about what's going on in your app and improve their mental model of your app's interface. Animations are especially useful when the screen changes state, such as when content loads or new actions become available. Animations can also add a polished look to your app, which gives your app a higher quality feel.

                      Keep in mind though, that overusing animations or using them at the wrong time can be detrimental, such as when they cause delays. This training class shows you how to implement some common types of animations that can increase usability and add flair without annoying your users.

                      Lessons

                      Crossfading Two Views
                      Learn how to crossfade between two overlapping views. This lesson shows you how to crossfade a progress indicator to a view that contains text content.
                      Using ViewPager for Screen Slides
                      Learn how to animate between horizontally adjacent screens with a sliding transition.
                      Displaying Card Flip Animations
                      Learn how to animate between two views with a flipping motion.
                      Zooming a View
                      Learn how to enlarge views with a touch-to-zoom animation.
                      Animating Layout Changes
                      Learn how to enable built-in animations when adding, removing, or updating child views in a layout.
                      This site uses cookies to store your preferences for site-specific language and display options.

                      This class requires API level or higher

                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                      Crossfading Two Views | Android Developers Skip to content

                      Most visited

                      Recently visited

                      navigation

                        Crossfading Two Views

                        This lesson teaches you to:

                        1. Create the Views
                        2. Set up the Animation
                        3. Crossfade the Views

                        Try it out

                        Download the sample app

                        Animations.zip

                        Crossfade animations (also know as dissolve) gradually fade out one UI component while simultaneously fading in another. This animation is useful for situations where you want to switch content or views in your app. Crossfades are very subtle and short but offer a fluid transition from one screen to the next. When you don't use them, however, transitions often feel abrupt or hurried.

                        Here's an example of a crossfade from a progress indicator to some text content.

                        Crossfade animation
                         

                        If you want to jump ahead and see a full working example, download and run the sample app and select the Crossfade example. See the following files for the code implementation:

                        • src/CrossfadeActivity.java
                        • layout/activity_crossfade.xml
                        • menu/activity_crossfade.xml

                        Create the Views

                        Create the two views that you want to crossfade. The following example creates a progress indicator and a scrollable text view:

                        <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent">
                        
                            <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                                android:id="@+id/content"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent">
                        
                                <TextView style="?android:textAppearanceMedium"
                                    android:lineSpacingMultiplier="1.2"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:text="@string/lorem_ipsum"
                                    android:padding="16dp" />
                        
                            </ScrollView>
                        
                            <ProgressBar android:id="@+id/loading_spinner"
                                style="?android:progressBarStyleLarge"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_gravity="center" />
                        
                        </FrameLayout>
                        

                        Set up the Animation

                        To set up the animation:

                        1. Create member variables for the views that you want to crossfade. You need these references later when modifying the views during the animation.
                        2. For the view that is being faded in, set its visibility to GONE. This prevents the view from taking up layout space and omits it from layout calculations, speeding up processing.
                        3. Cache the config_shortAnimTime system property in a member variable. This property defines a standard "short" duration for the animation. This duration is ideal for subtle animations or animations that occur very frequently. config_longAnimTime and config_mediumAnimTime are also available if you wish to use them.

                        Here's an example using the layout from the previous code snippet as the activity content view:

                        public class CrossfadeActivity extends Activity {
                        
                            private View mContentView;
                            private View mLoadingView;
                            private int mShortAnimationDuration;
                        
                            ...
                        
                            @Override
                            protected void onCreate(Bundle savedInstanceState) {
                                super.onCreate(savedInstanceState);
                                setContentView(R.layout.activity_crossfade);
                        
                                mContentView = findViewById(R.id.content);
                                mLoadingView = findViewById(R.id.loading_spinner);
                        
                                // Initially hide the content view.
                                mContentView.setVisibility(View.GONE);
                        
                                // Retrieve and cache the system's default "short" animation time.
                                mShortAnimationDuration = getResources().getInteger(
                                        android.R.integer.config_shortAnimTime);
                            }
                        
                        

                        Crossfade the Views

                        Now that the views are properly set up, crossfade them by doing the following:

                        1. For the view that is fading in, set the alpha value to 0 and the visibility to VISIBLE. (Remember that it was initially set to GONE.) This makes the view visible but completely transparent.
                        2. For the view that is fading in, animate its alpha value from 0 to 1. At the same time, for the view that is fading out, animate the alpha value from 1 to 0.
                        3. Using onAnimationEnd() in an Animator.AnimatorListener, set the visibility of the view that was fading out to GONE. Even though the alpha value is 0, setting the view's visibility to GONE prevents the view from taking up layout space and omits it from layout calculations, speeding up processing.

                        The following method shows an example of how to do this:

                        private View mContentView;
                        private View mLoadingView;
                        private int mShortAnimationDuration;
                        
                        ...
                        
                        private void crossfade() {
                        
                            // Set the content view to 0% opacity but visible, so that it is visible
                            // (but fully transparent) during the animation.
                            mContentView.setAlpha(0f);
                            mContentView.setVisibility(View.VISIBLE);
                        
                            // Animate the content view to 100% opacity, and clear any animation
                            // listener set on the view.
                            mContentView.animate()
                                    .alpha(1f)
                                    .setDuration(mShortAnimationDuration)
                                    .setListener(null);
                        
                            // Animate the loading view to 0% opacity. After the animation ends,
                            // set its visibility to GONE as an optimization step (it won't
                            // participate in layout passes, etc.)
                            mLoadingView.animate()
                                    .alpha(0f)
                                    .setDuration(mShortAnimationDuration)
                                    .setListener(new AnimatorListenerAdapter() {
                                        @Override
                                        public void onAnimationEnd(Animator animation) {
                                            mLoadingView.setVisibility(View.GONE);
                                        }
                                    });
                        }
                        
                        This site uses cookies to store your preferences for site-specific language and display options.

                        This class requires API level or higher

                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                        Using ViewPager for Screen Slides | Android Developers Skip to content

                        Most visited

                        Recently visited

                        navigation

                          Using ViewPager for Screen Slides

                          Screen slides are transitions between one entire screen to another and are common with UIs like setup wizards or slideshows. This lesson shows you how to do screen slides with a ViewPager provided by the support library. ViewPagers can animate screen slides automatically. Here's what a screen slide looks like that transitions from one screen of content to the next:

                          Screen slide animation
                           

                          If you want to jump ahead and see a full working example, download and run the sample app and select the Screen Slide example. See the following files for the code implementation:

                          • src/ScreenSlidePageFragment.java
                          • src/ScreenSlideActivity.java
                          • layout/activity_screen_slide.xml
                          • layout/fragment_screen_slide_page.xml

                          Create the Views

                          Create a layout file that you'll later use for the content of a fragment. The following example contains a text view to display some text:

                          <!-- fragment_screen_slide_page.xml -->
                          <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                              android:id="@+id/content"
                              android:layout_width="match_parent"
                              android:layout_height="match_parent" >
                          
                              <TextView style="?android:textAppearanceMedium"
                                  android:padding="16dp"
                                  android:lineSpacingMultiplier="1.2"
                                  android:layout_width="match_parent"
                                  android:layout_height="wrap_content"
                                  android:text="@string/lorem_ipsum" />
                          </ScrollView>
                          

                          Define also a string for the contents of the fragment.

                          Create the Fragment

                          Create a Fragment class that returns the layout that you just created in the onCreateView() method. You can then create instances of this fragment in the parent activity whenever you need a new page to display to the user:

                          import android.support.v4.app.Fragment;
                          ...
                          public class ScreenSlidePageFragment extends Fragment {
                          
                              @Override
                              public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                      Bundle savedInstanceState) {
                                  ViewGroup rootView = (ViewGroup) inflater.inflate(
                                          R.layout.fragment_screen_slide_page, container, false);
                          
                                  return rootView;
                              }
                          }
                          

                          Add a ViewPager

                          ViewPagers have built-in swipe gestures to transition through pages, and they display screen slide animations by default, so you don't need to create any. ViewPagers use PagerAdapters as a supply for new pages to display, so the PagerAdapter will use the fragment class that you created earlier.

                          To begin, create a layout that contains a ViewPager:

                          <!-- activity_screen_slide.xml -->
                          <android.support.v4.view.ViewPager
                              xmlns:android="http://schemas.android.com/apk/res/android"
                              android:id="@+id/pager"
                              android:layout_width="match_parent"
                              android:layout_height="match_parent" />
                          

                          Create an activity that does the following things:

                          • Sets the content view to be the layout with the ViewPager.
                          • Creates a class that extends the FragmentStatePagerAdapter abstract class and implements the getItem() method to supply instances of ScreenSlidePageFragment as new pages. The pager adapter also requires that you implement the getCount() method, which returns the amount of pages the adapter will create (five in the example).
                          • Hooks up the PagerAdapter to the ViewPager.
                          • Handles the device's back button by moving backwards in the virtual stack of fragments. If the user is already on the first page, go back on the activity back stack.
                          import android.support.v4.app.Fragment;
                          import android.support.v4.app.FragmentManager;
                          ...
                          public class ScreenSlidePagerActivity extends FragmentActivity {
                              /**
                               * The number of pages (wizard steps) to show in this demo.
                               */
                              private static final int NUM_PAGES = 5;
                          
                              /**
                               * The pager widget, which handles animation and allows swiping horizontally to access previous
                               * and next wizard steps.
                               */
                              private ViewPager mPager;
                          
                              /**
                               * The pager adapter, which provides the pages to the view pager widget.
                               */
                              private PagerAdapter mPagerAdapter;
                          
                              @Override
                              protected void onCreate(Bundle savedInstanceState) {
                                  super.onCreate(savedInstanceState);
                                  setContentView(R.layout.activity_screen_slide);
                          
                                  // Instantiate a ViewPager and a PagerAdapter.
                                  mPager = (ViewPager) findViewById(R.id.pager);
                                  mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
                                  mPager.setAdapter(mPagerAdapter);
                              }
                          
                              @Override
                              public void onBackPressed() {
                                  if (mPager.getCurrentItem() == 0) {
                                      // If the user is currently looking at the first step, allow the system to handle the
                                      // Back button. This calls finish() on this activity and pops the back stack.
                                      super.onBackPressed();
                                  } else {
                                      // Otherwise, select the previous step.
                                      mPager.setCurrentItem(mPager.getCurrentItem() - 1);
                                  }
                              }
                          
                              /**
                               * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
                               * sequence.
                               */
                              private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
                                  public ScreenSlidePagerAdapter(FragmentManager fm) {
                                      super(fm);
                                  }
                          
                                  @Override
                                  public Fragment getItem(int position) {
                                      return new ScreenSlidePageFragment();
                                  }
                          
                                  @Override
                                  public int getCount() {
                                      return NUM_PAGES;
                                  }
                              }
                          }
                          

                          Customize the Animation with PageTransformer

                          To display a different animation from the default screen slide animation, implement the ViewPager.PageTransformer interface and supply it to the view pager. The interface exposes a single method, transformPage(). At each point in the screen's transition, this method is called once for each visible page (generally there's only one visible page) and for adjacent pages just off the screen. For example, if page three is visible and the user drags towards page four, transformPage() is called for pages two, three, and four at each step of the gesture.

                          In your implementation of transformPage(), you can then create custom slide animations by determining which pages need to be transformed based on the position of the page on the screen, which is obtained from the position parameter of the transformPage() method.

                          The position parameter indicates where a given page is located relative to the center of the screen. It is a dynamic property that changes as the user scrolls through the pages. When a page fills the screen, its position value is 0. When a page is drawn just off the right side of the screen, its position value is 1. If the user scrolls halfway between pages one and two, page one has a position of -0.5 and page two has a position of 0.5. Based on the position of the pages on the screen, you can create custom slide animations by setting page properties with methods such as setAlpha(), setTranslationX(), or setScaleY().

                          When you have an implementation of a PageTransformer, call setPageTransformer() with your implementation to apply your custom animations. For example, if you have a PageTransformer named ZoomOutPageTransformer, you can set your custom animations like this:

                          ViewPager mPager = (ViewPager) findViewById(R.id.pager);
                          ...
                          mPager.setPageTransformer(true, new ZoomOutPageTransformer());
                          

                          See the Zoom-out page transformer and Depth page transformer sections for examples and videos of a PageTransformer.

                          Zoom-out page transformer

                          This page transformer shrinks and fades pages when scrolling between adjacent pages. As a page gets closer to the center, it grows back to its normal size and fades in.

                          ZoomOutPageTransformer example
                           
                          public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
                              private static final float MIN_SCALE = 0.85f;
                              private static final float MIN_ALPHA = 0.5f;
                          
                              public void transformPage(View view, float position) {
                                  int pageWidth = view.getWidth();
                                  int pageHeight = view.getHeight();
                          
                                  if (position < -1) { // [-Infinity,-1)
                                      // This page is way off-screen to the left.
                                      view.setAlpha(0);
                          
                                  } else if (position <= 1) { // [-1,1]
                                      // Modify the default slide transition to shrink the page as well
                                      float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
                                      float vertMargin = pageHeight * (1 - scaleFactor) / 2;
                                      float horzMargin = pageWidth * (1 - scaleFactor) / 2;
                                      if (position < 0) {
                                          view.setTranslationX(horzMargin - vertMargin / 2);
                                      } else {
                                          view.setTranslationX(-horzMargin + vertMargin / 2);
                                      }
                          
                                      // Scale the page down (between MIN_SCALE and 1)
                                      view.setScaleX(scaleFactor);
                                      view.setScaleY(scaleFactor);
                          
                                      // Fade the page relative to its size.
                                      view.setAlpha(MIN_ALPHA +
                                              (scaleFactor - MIN_SCALE) /
                                              (1 - MIN_SCALE) * (1 - MIN_ALPHA));
                          
                                  } else { // (1,+Infinity]
                                      // This page is way off-screen to the right.
                                      view.setAlpha(0);
                                  }
                              }
                          }
                          

                          Depth page transformer

                          This page transformer uses the default slide animation for sliding pages to the left, while using a "depth" animation for sliding pages to the right. This depth animation fades the page out, and scales it down linearly.

                          DepthPageTransformer example
                           

                          Note: During the depth animation, the default animation (a screen slide) still takes place, so you must counteract the screen slide with a negative X translation. For example:

                          view.setTranslationX(-1 * view.getWidth() * position);
                          
                          The following example shows how to counteract the default screen slide animation in a working page transformer:

                          
                          public class DepthPageTransformer implements ViewPager.PageTransformer {
                              private static final float MIN_SCALE = 0.75f;
                          
                              public void transformPage(View view, float position) {
                                  int pageWidth = view.getWidth();
                          
                                  if (position < -1) { // [-Infinity,-1)
                                      // This page is way off-screen to the left.
                                      view.setAlpha(0);
                          
                                  } else if (position <= 0) { // [-1,0]
                                      // Use the default slide transition when moving to the left page
                                      view.setAlpha(1);
                                      view.setTranslationX(0);
                                      view.setScaleX(1);
                                      view.setScaleY(1);
                          
                                  } else if (position <= 1) { // (0,1]
                                      // Fade the page out.
                                      view.setAlpha(1 - position);
                          
                                      // Counteract the default slide transition
                                      view.setTranslationX(pageWidth * -position);
                          
                                      // Scale the page down (between MIN_SCALE and 1)
                                      float scaleFactor = MIN_SCALE
                                              + (1 - MIN_SCALE) * (1 - Math.abs(position));
                                      view.setScaleX(scaleFactor);
                                      view.setScaleY(scaleFactor);
                          
                                  } else { // (1,+Infinity]
                                      // This page is way off-screen to the right.
                                      view.setAlpha(0);
                                  }
                              }
                          }
                          
                          This site uses cookies to store your preferences for site-specific language and display options.

                          This class requires API level or higher

                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                          Displaying Card Flip Animations | Android Developers Skip to content

                          Most visited

                          Recently visited

                          navigation

                            Displaying Card Flip Animations

                            This lesson shows you how to do a card flip animation with custom fragment animations. Card flips animate between views of content by showing an animation that emulates a card flipping over.

                            Here's what a card flip looks like:

                            Card flip animation
                             

                            If you want to jump ahead and see a full working example, download and run the sample app and select the Card Flip example. See the following files for the code implementation:

                            • src/CardFlipActivity.java
                            • animator/card_flip_right_in.xml
                            • animator/card_flip_right_out.xml
                            • animator/card_flip_left_in.xml
                            • animator/card_flip_left_out.xml
                            • layout/fragment_card_back.xml
                            • layout/fragment_card_front.xml

                            Create the Animators

                            Create the animations for the card flips. You'll need two animators for when the front of the card animates out and to the left and in and from the left. You'll also need two animators for when the back of the card animates in and from the right and out and to the right.

                            card_flip_left_in.xml

                            <set xmlns:android="http://schemas.android.com/apk/res/android">
                                <!-- Before rotating, immediately set the alpha to 0. -->
                                <objectAnimator
                                    android:valueFrom="1.0"
                                    android:valueTo="0.0"
                                    android:propertyName="alpha"
                                    android:duration="0" />
                            
                                <!-- Rotate. -->
                                <objectAnimator
                                    android:valueFrom="-180"
                                    android:valueTo="0"
                                    android:propertyName="rotationY"
                                    android:interpolator="@android:interpolator/accelerate_decelerate"
                                    android:duration="@integer/card_flip_time_full" />
                            
                                <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
                                <objectAnimator
                                    android:valueFrom="0.0"
                                    android:valueTo="1.0"
                                    android:propertyName="alpha"
                                    android:startOffset="@integer/card_flip_time_half"
                                    android:duration="1" />
                            </set>
                            

                            card_flip_left_out.xml

                            <set xmlns:android="http://schemas.android.com/apk/res/android">
                                <!-- Rotate. -->
                                <objectAnimator
                                    android:valueFrom="0"
                                    android:valueTo="180"
                                    android:propertyName="rotationY"
                                    android:interpolator="@android:interpolator/accelerate_decelerate"
                                    android:duration="@integer/card_flip_time_full" />
                            
                                <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
                                <objectAnimator
                                    android:valueFrom="1.0"
                                    android:valueTo="0.0"
                                    android:propertyName="alpha"
                                    android:startOffset="@integer/card_flip_time_half"
                                    android:duration="1" />
                            </set>
                            

                            card_flip_right_in.xml

                            <set xmlns:android="http://schemas.android.com/apk/res/android">
                                <!-- Before rotating, immediately set the alpha to 0. -->
                                <objectAnimator
                                    android:valueFrom="1.0"
                                    android:valueTo="0.0"
                                    android:propertyName="alpha"
                                    android:duration="0" />
                            
                                <!-- Rotate. -->
                                <objectAnimator
                                    android:valueFrom="180"
                                    android:valueTo="0"
                                    android:propertyName="rotationY"
                                    android:interpolator="@android:interpolator/accelerate_decelerate"
                                    android:duration="@integer/card_flip_time_full" />
                            
                                <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
                                <objectAnimator
                                    android:valueFrom="0.0"
                                    android:valueTo="1.0"
                                    android:propertyName="alpha"
                                    android:startOffset="@integer/card_flip_time_half"
                                    android:duration="1" />
                            </set>
                            

                            card_flip_right_out.xml

                            <set xmlns:android="http://schemas.android.com/apk/res/android">
                                <!-- Rotate. -->
                                <objectAnimator
                                    android:valueFrom="0"
                                    android:valueTo="-180"
                                    android:propertyName="rotationY"
                                    android:interpolator="@android:interpolator/accelerate_decelerate"
                                    android:duration="@integer/card_flip_time_full" />
                            
                                <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
                                <objectAnimator
                                    android:valueFrom="1.0"
                                    android:valueTo="0.0"
                                    android:propertyName="alpha"
                                    android:startOffset="@integer/card_flip_time_half"
                                    android:duration="1" />
                            </set>
                            

                            Create the Views

                            Each side of the "card" is a separate layout that can contain any content you want, such as two screens of text, two images, or any combination of views to flip between. You'll then use the two layouts in the fragments that you'll later animate. The following layouts create one side of a card that shows text:

                            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent"
                                android:orientation="vertical"
                                android:background="#a6c"
                                android:padding="16dp"
                                android:gravity="bottom">
                            
                                <TextView android:id="@android:id/text1"
                                    style="?android:textAppearanceLarge"
                                    android:textStyle="bold"
                                    android:textColor="#fff"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:text="@string/card_back_title" />
                            
                                <TextView style="?android:textAppearanceSmall"
                                    android:textAllCaps="true"
                                    android:textColor="#80ffffff"
                                    android:textStyle="bold"
                                    android:lineSpacingMultiplier="1.2"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:text="@string/card_back_description" />
                            
                            </LinearLayout>
                            

                            and the other side of the card that displays an ImageView:

                            <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent"
                                android:src="@drawable/image1"
                                android:scaleType="centerCrop"
                                android:contentDescription="@string/description_image_1" />
                            

                            Create the Fragment

                            Create fragment classes for the front and back of the card. These classes return the layouts that you created previously in the onCreateView() method of each fragment. You can then create instances of this fragment in the parent activity where you want to show the card. The following example shows nested fragment classes inside of the parent activity that uses them:

                            public class CardFlipActivity extends Activity {
                                ...
                                /**
                                 * A fragment representing the front of the card.
                                 */
                                public class CardFrontFragment extends Fragment {
                                    @Override
                                    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                            Bundle savedInstanceState) {
                                        return inflater.inflate(R.layout.fragment_card_front, container, false);
                                    }
                                }
                            
                                /**
                                 * A fragment representing the back of the card.
                                 */
                                public class CardBackFragment extends Fragment {
                                    @Override
                                    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                            Bundle savedInstanceState) {
                                        return inflater.inflate(R.layout.fragment_card_back, container, false);
                                    }
                                }
                            }
                            

                            Animate the Card Flip

                            Now, you'll need to display the fragments inside of a parent activity. To do this, first create the layout for your activity. The following example creates a FrameLayout that you can add fragments to at runtime:

                            <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                android:id="@+id/container"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent" />
                            

                            In the activity code, set the content view to be the layout that you just created. It's also good idea to show a default fragment when the activity is created, so the following example activity shows you how to display the front of the card by default:

                            public class CardFlipActivity extends Activity {
                            
                                @Override
                                protected void onCreate(Bundle savedInstanceState) {
                                    super.onCreate(savedInstanceState);
                                    setContentView(R.layout.activity_activity_card_flip);
                            
                                    if (savedInstanceState == null) {
                                        getFragmentManager()
                                                .beginTransaction()
                                                .add(R.id.container, new CardFrontFragment())
                                                .commit();
                                    }
                                }
                                ...
                            }
                            

                            Now that you have the front of the card showing, you can show the back of the card with the flip animation at an appropriate time. Create a method to show the other side of the card that does the following things:

                            • Sets the custom animations that you created earlier for the fragment transitions.
                            • Replaces the currently displayed fragment with a new fragment and animates this event with the custom animations that you created.
                            • Adds the previously displayed fragment to the fragment back stack so when the user presses the Back button, the card flips back over.
                            private void flipCard() {
                                if (mShowingBack) {
                                    getFragmentManager().popBackStack();
                                    return;
                                }
                            
                                // Flip to the back.
                            
                                mShowingBack = true;
                            
                                // Create and commit a new fragment transaction that adds the fragment for
                                // the back of the card, uses custom animations, and is part of the fragment
                                // manager's back stack.
                            
                                getFragmentManager()
                                        .beginTransaction()
                            
                                        // Replace the default fragment animations with animator resources
                                        // representing rotations when switching to the back of the card, as
                                        // well as animator resources representing rotations when flipping
                                        // back to the front (e.g. when the system Back button is pressed).
                                        .setCustomAnimations(
                                                R.animator.card_flip_right_in,
                                                R.animator.card_flip_right_out,
                                                R.animator.card_flip_left_in,
                                                R.animator.card_flip_left_out)
                            
                                        // Replace any fragments currently in the container view with a
                                        // fragment representing the next page (indicated by the
                                        // just-incremented currentPage variable).
                                        .replace(R.id.container, new CardBackFragment())
                            
                                        // Add this transaction to the back stack, allowing users to press
                                        // Back to get to the front of the card.
                                        .addToBackStack(null)
                            
                                        // Commit the transaction.
                                        .commit();
                            }
                            
                            This site uses cookies to store your preferences for site-specific language and display options.

                            This class requires API level or higher

                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                            Zooming a View | Android Developers Skip to content

                            Most visited

                            Recently visited

                            navigation

                              Zooming a View

                              This lesson teaches you to:

                              1. Create the Views
                              2. Set up the Zoom Animation
                              3. Zoom the View

                              Try it out

                              Download the sample app

                              Animations.zip

                              This lesson demonstrates how to do a touch-to-zoom animation, which is useful for apps such as photo galleries to animate a view from a thumbnail to a full-size image that fills the screen.

                              Here's what a touch-to-zoom animation looks like that expands an image thumbnail to fill the screen:

                              Zoom animation
                               

                              If you want to jump ahead and see a full working example, download and run the sample app and select the Zoom example. See the following files for the code implementation:

                              • src/TouchHighlightImageButton.java (a simple helper class that shows a blue touch highlight when the image button is pressed)
                              • src/ZoomActivity.java
                              • layout/activity_zoom.xml

                              Create the Views

                              Create a layout file that contains the small and large version of the content that you want to zoom. The following example creates an ImageButton for clickable image thumbnail and an ImageView that displays the enlarged view of the image:

                              <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                  android:id="@+id/container"
                                  android:layout_width="match_parent"
                                  android:layout_height="match_parent">
                              
                                  <LinearLayout android:layout_width="match_parent"
                                      android:layout_height="wrap_content"
                                      android:orientation="vertical"
                                      android:padding="16dp">
                              
                                      <ImageButton
                                          android:id="@+id/thumb_button_1"
                                          android:layout_width="100dp"
                                          android:layout_height="75dp"
                                          android:layout_marginRight="1dp"
                                          android:src="@drawable/thumb1"
                                          android:scaleType="centerCrop"
                                          android:contentDescription="@string/description_image_1" />
                              
                                  </LinearLayout>
                              
                                  <!-- This initially-hidden ImageView will hold the expanded/zoomed version of
                                       the images above. Without transformations applied, it takes up the entire
                                       screen. To achieve the "zoom" animation, this view's bounds are animated
                                       from the bounds of the thumbnail button above, to its final laid-out
                                       bounds.
                                       -->
                              
                                  <ImageView
                                      android:id="@+id/expanded_image"
                                      android:layout_width="match_parent"
                                      android:layout_height="match_parent"
                                      android:visibility="invisible"
                                      android:contentDescription="@string/description_zoom_touch_close" />
                              
                              </FrameLayout>
                              

                              Set up the Zoom Animation

                              Once you apply your layout, set up the event handlers that trigger the zoom animation. The following example adds a View.OnClickListener to the ImageButton to execute the zoom animation when the user clicks the image button:

                              public class ZoomActivity extends FragmentActivity {
                                  // Hold a reference to the current animator,
                                  // so that it can be canceled mid-way.
                                  private Animator mCurrentAnimator;
                              
                                  // The system "short" animation time duration, in milliseconds. This
                                  // duration is ideal for subtle animations or animations that occur
                                  // very frequently.
                                  private int mShortAnimationDuration;
                              
                                  @Override
                                  protected void onCreate(Bundle savedInstanceState) {
                                      super.onCreate(savedInstanceState);
                                      setContentView(R.layout.activity_zoom);
                              
                                      // Hook up clicks on the thumbnail views.
                              
                                      final View thumb1View = findViewById(R.id.thumb_button_1);
                                      thumb1View.setOnClickListener(new View.OnClickListener() {
                                          @Override
                                          public void onClick(View view) {
                                              zoomImageFromThumb(thumb1View, R.drawable.image1);
                                          }
                                      });
                              
                                      // Retrieve and cache the system's default "short" animation time.
                                      mShortAnimationDuration = getResources().getInteger(
                                              android.R.integer.config_shortAnimTime);
                                  }
                                  ...
                              }
                              

                              Zoom the View

                              You'll now need to animate from the normal sized view to the zoomed view when appropriate. In general, you need to animate from the bounds of the normal-sized view to the bounds of the larger-sized view. The following method shows you how to implement a zoom animation that zooms from an image thumbnail to an enlarged view by doing the following things:

                              1. Assign the high-res image to the hidden "zoomed-in" (enlarged) ImageView. The following example loads a large image resource on the UI thread for simplicity. You will want to do this loading in a separate thread to prevent blocking on the UI thread and then set the bitmap on the UI thread. Ideally, the bitmap should not be larger than the screen size.
                              2. Calculate the starting and ending bounds for the ImageView.
                              3. Animate each of the four positioning and sizing properties X, Y, (SCALE_X, and SCALE_Y) simultaneously, from the starting bounds to the ending bounds. These four animations are added to an AnimatorSet so that they can be started at the same time.
                              4. Zoom back out by running a similar animation but in reverse when the user touches the screen when the image is zoomed in. You can do this by adding a View.OnClickListener to the ImageView. When clicked, the ImageView minimizes back down to the size of the image thumbnail and sets its visibility to GONE to hide it.
                              private void zoomImageFromThumb(final View thumbView, int imageResId) {
                                  // If there's an animation in progress, cancel it
                                  // immediately and proceed with this one.
                                  if (mCurrentAnimator != null) {
                                      mCurrentAnimator.cancel();
                                  }
                              
                                  // Load the high-resolution "zoomed-in" image.
                                  final ImageView expandedImageView = (ImageView) findViewById(
                                          R.id.expanded_image);
                                  expandedImageView.setImageResource(imageResId);
                              
                                  // Calculate the starting and ending bounds for the zoomed-in image.
                                  // This step involves lots of math. Yay, math.
                                  final Rect startBounds = new Rect();
                                  final Rect finalBounds = new Rect();
                                  final Point globalOffset = new Point();
                              
                                  // The start bounds are the global visible rectangle of the thumbnail,
                                  // and the final bounds are the global visible rectangle of the container
                                  // view. Also set the container view's offset as the origin for the
                                  // bounds, since that's the origin for the positioning animation
                                  // properties (X, Y).
                                  thumbView.getGlobalVisibleRect(startBounds);
                                  findViewById(R.id.container)
                                          .getGlobalVisibleRect(finalBounds, globalOffset);
                                  startBounds.offset(-globalOffset.x, -globalOffset.y);
                                  finalBounds.offset(-globalOffset.x, -globalOffset.y);
                              
                                  // Adjust the start bounds to be the same aspect ratio as the final
                                  // bounds using the "center crop" technique. This prevents undesirable
                                  // stretching during the animation. Also calculate the start scaling
                                  // factor (the end scaling factor is always 1.0).
                                  float startScale;
                                  if ((float) finalBounds.width() / finalBounds.height()
                                          > (float) startBounds.width() / startBounds.height()) {
                                      // Extend start bounds horizontally
                                      startScale = (float) startBounds.height() / finalBounds.height();
                                      float startWidth = startScale * finalBounds.width();
                                      float deltaWidth = (startWidth - startBounds.width()) / 2;
                                      startBounds.left -= deltaWidth;
                                      startBounds.right += deltaWidth;
                                  } else {
                                      // Extend start bounds vertically
                                      startScale = (float) startBounds.width() / finalBounds.width();
                                      float startHeight = startScale * finalBounds.height();
                                      float deltaHeight = (startHeight - startBounds.height()) / 2;
                                      startBounds.top -= deltaHeight;
                                      startBounds.bottom += deltaHeight;
                                  }
                              
                                  // Hide the thumbnail and show the zoomed-in view. When the animation
                                  // begins, it will position the zoomed-in view in the place of the
                                  // thumbnail.
                                  thumbView.setAlpha(0f);
                                  expandedImageView.setVisibility(View.VISIBLE);
                              
                                  // Set the pivot point for SCALE_X and SCALE_Y transformations
                                  // to the top-left corner of the zoomed-in view (the default
                                  // is the center of the view).
                                  expandedImageView.setPivotX(0f);
                                  expandedImageView.setPivotY(0f);
                              
                                  // Construct and run the parallel animation of the four translation and
                                  // scale properties (X, Y, SCALE_X, and SCALE_Y).
                                  AnimatorSet set = new AnimatorSet();
                                  set
                                          .play(ObjectAnimator.ofFloat(expandedImageView, View.X,
                                                  startBounds.left, finalBounds.left))
                                          .with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
                                                  startBounds.top, finalBounds.top))
                                          .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,
                                          startScale, 1f)).with(ObjectAnimator.ofFloat(expandedImageView,
                                                  View.SCALE_Y, startScale, 1f));
                                  set.setDuration(mShortAnimationDuration);
                                  set.setInterpolator(new DecelerateInterpolator());
                                  set.addListener(new AnimatorListenerAdapter() {
                                      @Override
                                      public void onAnimationEnd(Animator animation) {
                                          mCurrentAnimator = null;
                                      }
                              
                                      @Override
                                      public void onAnimationCancel(Animator animation) {
                                          mCurrentAnimator = null;
                                      }
                                  });
                                  set.start();
                                  mCurrentAnimator = set;
                              
                                  // Upon clicking the zoomed-in image, it should zoom back down
                                  // to the original bounds and show the thumbnail instead of
                                  // the expanded image.
                                  final float startScaleFinal = startScale;
                                  expandedImageView.setOnClickListener(new View.OnClickListener() {
                                      @Override
                                      public void onClick(View view) {
                                          if (mCurrentAnimator != null) {
                                              mCurrentAnimator.cancel();
                                          }
                              
                                          // Animate the four positioning/sizing properties in parallel,
                                          // back to their original values.
                                          AnimatorSet set = new AnimatorSet();
                                          set.play(ObjectAnimator
                                                      .ofFloat(expandedImageView, View.X, startBounds.left))
                                                      .with(ObjectAnimator
                                                              .ofFloat(expandedImageView, 
                                                                      View.Y,startBounds.top))
                                                      .with(ObjectAnimator
                                                              .ofFloat(expandedImageView, 
                                                                      View.SCALE_X, startScaleFinal))
                                                      .with(ObjectAnimator
                                                              .ofFloat(expandedImageView, 
                                                                      View.SCALE_Y, startScaleFinal));
                                          set.setDuration(mShortAnimationDuration);
                                          set.setInterpolator(new DecelerateInterpolator());
                                          set.addListener(new AnimatorListenerAdapter() {
                                              @Override
                                              public void onAnimationEnd(Animator animation) {
                                                  thumbView.setAlpha(1f);
                                                  expandedImageView.setVisibility(View.GONE);
                                                  mCurrentAnimator = null;
                                              }
                              
                                              @Override
                                              public void onAnimationCancel(Animator animation) {
                                                  thumbView.setAlpha(1f);
                                                  expandedImageView.setVisibility(View.GONE);
                                                  mCurrentAnimator = null;
                                              }
                                          });
                                          set.start();
                                          mCurrentAnimator = set;
                                      }
                                  });
                              }
                              
                              This site uses cookies to store your preferences for site-specific language and display options.

                              This class requires API level or higher

                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                              Animating Layout Changes | Android Developers Skip to content

                              Most visited

                              Recently visited

                              navigation

                                Animating Layout Changes

                                This lesson teaches you to:

                                1. Create the Layout
                                2. Add, Update, or Remove Items from the Layout

                                Try it out

                                Download the sample app

                                Animations.zip

                                A layout animation is a pre-loaded animation that the system runs each time you make a change to the layout configuration. All you need to do is set an attribute in the layout to tell the Android system to animate these layout changes, and system-default animations are carried out for you.

                                Tip: If you want to supply custom layout animations, create a LayoutTransition object and supply it to the layout with the setLayoutTransition() method.

                                Here's what a default layout animation looks like when adding items to a list:

                                Layout animation
                                 

                                If you want to jump ahead and see a full working example, download and run the sample app and select the Crossfade example. See the following files for the code implementation:

                                1. src/LayoutChangesActivity.java
                                2. layout/activity_layout_changes.xml
                                3. menu/activity_layout_changes.xml

                                Create the Layout

                                In your activity's layout XML file, set the android:animateLayoutChanges attribute to true for the layout that you want to enable animations for. For instance:

                                <LinearLayout android:id="@+id/container"
                                    android:animateLayoutChanges="true"
                                    ...
                                />
                                

                                Add, Update, or Remove Items from the Layout

                                Now, all you need to do is add, remove, or update items in the layout and the items are animated automatically:

                                private ViewGroup mContainerView;
                                ...
                                private void addItem() {
                                    View newView;
                                    ...
                                    mContainerView.addView(newView, 0);
                                }
                                
                                This site uses cookies to store your preferences for site-specific language and display options.

                                This class requires API level or higher

                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                Building Apps with Connectivity & the Cloud | Android Developers Skip to content

                                Most visited

                                Recently visited

                                navigation

                                  Building Apps with Connectivity & the Cloud

                                  These classes teach you how to connect your app to the world beyond the user's device. You'll learn how to connect to other devices in the area, connect to the Internet, backup and sync your app's data, and more.

                                  This site uses cookies to store your preferences for site-specific language and display options.

                                  This class requires API level or higher

                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                  Connecting Devices Wirelessly | Android Developers Skip to content

                                  Most visited

                                  Recently visited

                                  navigation

                                    Connecting Devices Wirelessly

                                    Dependencies and prerequisites

                                    • Android 4.1 or higher

                                    You should also read

                                    Video

                                    DevBytes: Network Service Discovery

                                    Besides enabling communication with the cloud, Android's wireless APIs also enable communication with other devices on the same local network, and even devices which are not on a network, but are physically nearby. The addition of Network Service Discovery (NSD) takes this further by allowing an application to seek out a nearby device running services with which it can communicate. Integrating this functionality into your application helps you provide a wide range of features, such as playing games with users in the same room, pulling images from a networked NSD-enabled webcam, or remotely logging into other machines on the same network.

                                    This class describes the key APIs for finding and connecting to other devices from your application. Specifically, it describes the NSD API for discovering available services and the Wi-Fi Peer-to-Peer (P2P) API for doing peer-to-peer wireless connections. This class also shows you how to use NSD and Wi-Fi P2P in combination to detect the services offered by a device and connect to the device when neither device is connected to a network.

                                    Lessons

                                    Using Network Service Discovery
                                    Learn how to broadcast services offered by your own application, discover services offered on the local network, and use NSD to determine the connection details for the service you wish to connect to.
                                    Creating P2P Connections with Wi-Fi
                                    Learn how to fetch a list of nearby peer devices, create an access point for legacy devices, and connect to other devices capable of Wi-Fi P2P connections.
                                    Using Wi-Fi P2P for Service Discovery
                                    Learn how to discover services published by nearby devices without being on the same network, using Wi-Fi P2P.
                                    This site uses cookies to store your preferences for site-specific language and display options.

                                    This class requires API level or higher

                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                    Performing Network Operations | Android Developers Skip to content

                                    Most visited

                                    Recently visited

                                    navigation

                                      Performing Network Operations

                                      Dependencies and prerequisites

                                      • Android 1.6 (API level 4) or higher
                                      • A device that is able to connect to mobile and Wi-Fi networks

                                      You should also read

                                      Try it out

                                      Download the sample

                                      NetworkUsage.zip

                                      This class explains the basic tasks involved in connecting to the network, monitoring the network connection (including connection changes), and giving users control over an app's network usage. It also describes how to parse and consume XML data.

                                      This class includes a sample application that illustrates how to perform common network operations. You can download the sample (to the right) and use it as a source of reusable code for your own application.

                                      By going through these lessons, you'll have the fundamental building blocks for creating Android applications that download content and parse data efficiently, while minimizing network traffic.

                                      Note: See the class Transmitting Network Data Using Volley for information on Volley, an HTTP library that makes networking for Android apps easier and faster. Volley is available through the open AOSP repository. Volley may be able to help you streamline and improve the performance of your app's network operations.

                                      Lessons

                                      Connecting to the Network
                                      Learn how to connect to the network, choose an HTTP client, and perform network operations outside of the UI thread.
                                      Managing Network Usage
                                      Learn how to check a device's network connection, create a preferences UI for controlling network usage, and respond to connection changes.
                                      Parsing XML Data
                                      Learn how to parse and consume XML data.
                                      This site uses cookies to store your preferences for site-specific language and display options.

                                      This class requires API level or higher

                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                      Connecting to the Network | Android Developers Skip to content

                                      Most visited

                                      Recently visited

                                      navigation

                                        Connecting to the Network

                                        This lesson shows you how to implement a simple application that connects to the network. It explains some of the best practices you should follow in creating even the simplest network-connected app.

                                        Note that to perform the network operations described in this lesson, your application manifest must include the following permissions:

                                        <uses-permission android:name="android.permission.INTERNET" />
                                        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

                                        Choose an HTTP Client

                                        Most network-connected Android apps use HTTP to send and receive data. The Android platform includes the HttpURLConnection client, which supports HTTPS, streaming uploads and downloads, configurable timeouts, IPv6, and connection pooling.

                                        Check the Network Connection

                                        Before your app attempts to connect to the network, it should check to see whether a network connection is available using getActiveNetworkInfo() and isConnected(). Remember, the device may be out of range of a network, or the user may have disabled both Wi-Fi and mobile data access. For more discussion of this topic, see the lesson Managing Network Usage.

                                        public void myClickHandler(View view) {
                                            ...
                                            ConnectivityManager connMgr = (ConnectivityManager) 
                                                getSystemService(Context.CONNECTIVITY_SERVICE);
                                            NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
                                            if (networkInfo != null && networkInfo.isConnected()) {
                                                // fetch data
                                            } else {
                                                // display error
                                            }
                                            ...
                                        }

                                        Perform Network Operations on a Separate Thread

                                        Network operations can involve unpredictable delays. To prevent this from causing a poor user experience, always perform network operations on a separate thread from the UI. The AsyncTask class provides one of the simplest ways to fire off a new task from the UI thread. For more discussion of this topic, see the blog post Multithreading For Performance.

                                        In the following snippet, the myClickHandler() method invokes new DownloadWebpageTask().execute(stringUrl). The DownloadWebpageTask class is a subclass of AsyncTask. DownloadWebpageTask implements the following AsyncTask methods:

                                        • doInBackground() executes the method downloadUrl(). It passes the web page URL as a parameter. The method downloadUrl() fetches and processes the web page content. When it finishes, it passes back a result string.
                                        • onPostExecute() takes the returned string and displays it in the UI.
                                        public class HttpExampleActivity extends Activity {
                                            private static final String DEBUG_TAG = "HttpExample";
                                            private EditText urlText;
                                            private TextView textView;
                                            
                                            @Override
                                            public void onCreate(Bundle savedInstanceState) {
                                                super.onCreate(savedInstanceState);
                                                setContentView(R.layout.main);   
                                                urlText = (EditText) findViewById(R.id.myUrl);
                                                textView = (TextView) findViewById(R.id.myText);
                                            }
                                        
                                            // When user clicks button, calls AsyncTask.
                                            // Before attempting to fetch the URL, makes sure that there is a network connection.
                                            public void myClickHandler(View view) {
                                                // Gets the URL from the UI's text field.
                                                String stringUrl = urlText.getText().toString();
                                                ConnectivityManager connMgr = (ConnectivityManager) 
                                                    getSystemService(Context.CONNECTIVITY_SERVICE);
                                                NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
                                                if (networkInfo != null && networkInfo.isConnected()) {
                                                    new DownloadWebpageTask().execute(stringUrl);
                                                } else {
                                                    textView.setText("No network connection available.");
                                                }
                                            }
                                        
                                             // Uses AsyncTask to create a task away from the main UI thread. This task takes a 
                                             // URL string and uses it to create an HttpUrlConnection. Once the connection
                                             // has been established, the AsyncTask downloads the contents of the webpage as
                                             // an InputStream. Finally, the InputStream is converted into a string, which is
                                             // displayed in the UI by the AsyncTask's onPostExecute method.
                                             private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
                                                @Override
                                                protected String doInBackground(String... urls) {
                                                      
                                                    // params comes from the execute() call: params[0] is the url.
                                                    try {
                                                        return downloadUrl(urls[0]);
                                                    } catch (IOException e) {
                                                        return "Unable to retrieve web page. URL may be invalid.";
                                                    }
                                                }
                                                // onPostExecute displays the results of the AsyncTask.
                                                @Override
                                                protected void onPostExecute(String result) {
                                                    textView.setText(result);
                                               }
                                            }
                                            ...
                                        }

                                        The sequence of events in this snippet is as follows:

                                        1. When users click the button that invokes myClickHandler(), the app passes the specified URL to the AsyncTask subclass DownloadWebpageTask.
                                        2. The AsyncTask method doInBackground() calls the downloadUrl() method.
                                        3. The downloadUrl() method takes a URL string as a parameter and uses it to create a URL object.
                                        4. The URL object is used to establish an HttpURLConnection.
                                        5. Once the connection has been established, the HttpURLConnection object fetches the web page content as an InputStream.
                                        6. The InputStream is passed to the readIt() method, which converts the stream to a string.
                                        7. Finally, the AsyncTask's onPostExecute() method displays the string in the main activity's UI.

                                        Connect and Download Data

                                        In your thread that performs your network transactions, you can use HttpURLConnection to perform a GET and download your data. After you call connect(), you can get an InputStream of the data by calling getInputStream().

                                        In the following snippet, the doInBackground() method calls the method downloadUrl(). The downloadUrl() method takes the given URL and uses it to connect to the network via HttpURLConnection. Once a connection has been established, the app uses the method getInputStream() to retrieve the data as an InputStream.

                                        // Given a URL, establishes an HttpUrlConnection and retrieves
                                        // the web page content as a InputStream, which it returns as
                                        // a string.
                                        private String downloadUrl(String myurl) throws IOException {
                                            InputStream is = null;
                                            // Only display the first 500 characters of the retrieved
                                            // web page content.
                                            int len = 500;
                                                
                                            try {
                                                URL url = new URL(myurl);
                                                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                                                conn.setReadTimeout(10000 /* milliseconds */);
                                                conn.setConnectTimeout(15000 /* milliseconds */);
                                                conn.setRequestMethod("GET");
                                                conn.setDoInput(true);
                                                // Starts the query
                                                conn.connect();
                                                int response = conn.getResponseCode();
                                                Log.d(DEBUG_TAG, "The response is: " + response);
                                                is = conn.getInputStream();
                                        
                                                // Convert the InputStream into a string
                                                String contentAsString = readIt(is, len);
                                                return contentAsString;
                                                
                                            // Makes sure that the InputStream is closed after the app is
                                            // finished using it.
                                            } finally {
                                                if (is != null) {
                                                    is.close();
                                                } 
                                            }
                                        }

                                        Note that the method getResponseCode() returns the connection's status code. This is a useful way of getting additional information about the connection. A status code of 200 indicates success.

                                        Convert the InputStream to a String

                                        An InputStream is a readable source of bytes. Once you get an InputStream, it's common to decode or convert it into a target data type. For example, if you were downloading image data, you might decode and display it like this:

                                        InputStream is = null;
                                        ...
                                        Bitmap bitmap = BitmapFactory.decodeStream(is);
                                        ImageView imageView = (ImageView) findViewById(R.id.image_view);
                                        imageView.setImageBitmap(bitmap);
                                        

                                        In the example shown above, the InputStream represents the text of a web page. This is how the example converts the InputStream to a string so that the activity can display it in the UI:

                                        // Reads an InputStream and converts it to a String.
                                        public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
                                            Reader reader = null;
                                            reader = new InputStreamReader(stream, "UTF-8");        
                                            char[] buffer = new char[len];
                                            reader.read(buffer);
                                            return new String(buffer);
                                        }
                                        This site uses cookies to store your preferences for site-specific language and display options.

                                        This class requires API level or higher

                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                        Managing Network Usage | Android Developers Skip to content

                                        Most visited

                                        Recently visited

                                        navigation

                                          Managing Network Usage

                                          This lesson describes how to write applications that have fine-grained control over their usage of network resources. If your application performs a lot of network operations, you should provide user settings that allow users to control your app’s data habits, such as how often your app syncs data, whether to perform uploads/downloads only when on Wi-Fi, whether to use data while roaming, and so on. With these controls available to them, users are much less likely to disable your app’s access to background data when they approach their limits, because they can instead precisely control how much data your app uses.

                                          For general guidelines on how to write apps that minimize the battery life impact of downloads and network connections, see Optimizing Battery Life and Transferring Data Without Draining the Battery.

                                          Check a Device's Network Connection

                                          A device can have various types of network connections. This lesson focuses on using either a Wi-Fi or a mobile network connection. For the full list of possible network types, see ConnectivityManager.

                                          Wi-Fi is typically faster. Also, mobile data is often metered, which can get expensive. A common strategy for apps is to only fetch large data if a Wi-Fi network is available.

                                          Before you perform network operations, it's good practice to check the state of network connectivity. Among other things, this could prevent your app from inadvertently using the wrong radio. If a network connection is unavailable, your application should respond gracefully. To check the network connection, you typically use the following classes:

                                          • ConnectivityManager: Answers queries about the state of network connectivity. It also notifies applications when network connectivity changes.
                                          • NetworkInfo: Describes the status of a network interface of a given type (currently either Mobile or Wi-Fi).

                                          This code snippet tests network connectivity for Wi-Fi and mobile. It determines whether these network interfaces are available (that is, whether network connectivity is possible) and/or connected (that is, whether network connectivity exists and if it is possible to establish sockets and pass data):

                                          private static final String DEBUG_TAG = "NetworkStatusExample";
                                          ...      
                                          ConnectivityManager connMgr = (ConnectivityManager) 
                                                  getSystemService(Context.CONNECTIVITY_SERVICE);
                                          NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 
                                          boolean isWifiConn = networkInfo.isConnected();
                                          networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
                                          boolean isMobileConn = networkInfo.isConnected();
                                          Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
                                          Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
                                          

                                          Note that you should not base decisions on whether a network is "available." You should always check isConnected() before performing network operations, since isConnected() handles cases like flaky mobile networks, airplane mode, and restricted background data.

                                          A more concise way of checking whether a network interface is available is as follows. The method getActiveNetworkInfo() returns a NetworkInfo instance representing the first connected network interface it can find, or null if none of the interfaces is connected (meaning that an internet connection is not available):

                                          public boolean isOnline() {
                                              ConnectivityManager connMgr = (ConnectivityManager) 
                                                      getSystemService(Context.CONNECTIVITY_SERVICE);
                                              NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
                                              return (networkInfo != null && networkInfo.isConnected());
                                          }  

                                          To query more fine-grained state you can use NetworkInfo.DetailedState, but this should seldom be necessary.

                                          Manage Network Usage

                                          You can implement a preferences activity that gives users explicit control over your app's usage of network resources. For example:

                                          • You might allow users to upload videos only when the device is connected to a Wi-Fi network.
                                          • You might sync (or not) depending on specific criteria such as network availability, time interval, and so on.

                                          To write an app that supports network access and managing network usage, your manifest must have the right permissions and intent filters.

                                          • The manifest excerpted below includes the following permissions:
                                          • You can declare the intent filter for the ACTION_MANAGE_NETWORK_USAGE action (introduced in Android 4.0) to indicate that your application defines an activity that offers options to control data usage. ACTION_MANAGE_NETWORK_USAGE shows settings for managing the network data usage of a specific application. When your app has a settings activity that allows users to control network usage, you should declare this intent filter for that activity. In the sample application, this action is handled by the class SettingsActivity, which displays a preferences UI to let users decide when to download a feed.
                                          <?xml version="1.0" encoding="utf-8"?>
                                          <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                              package="com.example.android.networkusage"
                                              ...>
                                          
                                              <uses-sdk android:minSdkVersion="4" 
                                                     android:targetSdkVersion="14" />
                                                  
                                              <uses-permission android:name="android.permission.INTERNET" />
                                              <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
                                          
                                              <application
                                                  ...>
                                                  ...
                                                  <activity android:label="SettingsActivity" android:name=".SettingsActivity">
                                                       <intent-filter>
                                                          <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
                                                          <category android:name="android.intent.category.DEFAULT" />
                                                    </intent-filter>
                                                  </activity>
                                              </application>
                                          </manifest>
                                          

                                          Implement a Preferences Activity

                                          As you can see in the manifest excerpt above, the sample app's activity SettingsActivity has an intent filter for the ACTION_MANAGE_NETWORK_USAGE action. SettingsActivity is a subclass of PreferenceActivity. It displays a preferences screen (shown in figure 1) that lets users specify the following:

                                          • Whether to display summaries for each XML feed entry, or just a link for each entry.
                                          • Whether to download the XML feed if any network connection is available, or only if Wi-Fi is available.
                                          Preferences panel Setting a network preference

                                          Figure 1. Preferences activity.

                                          Here is SettingsActivity. Note that it implements OnSharedPreferenceChangeListener. When a user changes a preference, it fires onSharedPreferenceChanged(), which sets refreshDisplay to true. This causes the display to refresh when the user returns to the main activity:

                                          public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
                                              
                                              @Override
                                              protected void onCreate(Bundle savedInstanceState) {
                                                  super.onCreate(savedInstanceState);
                                                  
                                                  // Loads the XML preferences file
                                                  addPreferencesFromResource(R.xml.preferences);
                                              }
                                            
                                              @Override
                                              protected void onResume() {
                                                  super.onResume();
                                          
                                                  // Registers a listener whenever a key changes            
                                                  getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
                                              }
                                            
                                              @Override
                                              protected void onPause() {
                                                  super.onPause();
                                          
                                                 // Unregisters the listener set in onResume().
                                                 // It's best practice to unregister listeners when your app isn't using them to cut down on 
                                                 // unnecessary system overhead. You do this in onPause().            
                                                 getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);    
                                              }
                                            
                                              // When the user changes the preferences selection, 
                                              // onSharedPreferenceChanged() restarts the main activity as a new
                                              // task. Sets the refreshDisplay flag to "true" to indicate that
                                              // the main activity should update its display.
                                              // The main activity queries the PreferenceManager to get the latest settings.
                                              
                                              @Override
                                              public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {    
                                                  // Sets refreshDisplay to true so that when the user returns to the main
                                                  // activity, the display refreshes to reflect the new settings.
                                                  NetworkActivity.refreshDisplay = true;
                                              }
                                          }

                                          Respond to Preference Changes

                                          When the user changes preferences in the settings screen, it typically has consequences for the app's behavior. In this snippet, the app checks the preferences settings in onStart(). if there is a match between the setting and the device's network connection (for example, if the setting is "Wi-Fi" and the device has a Wi-Fi connection), the app downloads the feed and refreshes the display.

                                          public class NetworkActivity extends Activity {
                                              public static final String WIFI = "Wi-Fi";
                                              public static final String ANY = "Any";
                                              private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
                                             
                                              // Whether there is a Wi-Fi connection.
                                              private static boolean wifiConnected = false; 
                                              // Whether there is a mobile connection.
                                              private static boolean mobileConnected = false;
                                              // Whether the display should be refreshed.
                                              public static boolean refreshDisplay = true;
                                              
                                              // The user's current network preference setting.
                                              public static String sPref = null;
                                              
                                              // The BroadcastReceiver that tracks network connectivity changes.
                                              private NetworkReceiver receiver = new NetworkReceiver();
                                              
                                              @Override
                                              public void onCreate(Bundle savedInstanceState) {
                                                  super.onCreate(savedInstanceState);
                                                  
                                                  // Registers BroadcastReceiver to track network connection changes.
                                                  IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
                                                  receiver = new NetworkReceiver();
                                                  this.registerReceiver(receiver, filter);
                                              }
                                              
                                              @Override 
                                              public void onDestroy() {
                                                  super.onDestroy();
                                                  // Unregisters BroadcastReceiver when app is destroyed.
                                                  if (receiver != null) {
                                                      this.unregisterReceiver(receiver);
                                                  }
                                              }
                                              
                                              // Refreshes the display if the network connection and the
                                              // pref settings allow it.
                                              
                                              @Override
                                              public void onStart () {
                                                  super.onStart();  
                                                  
                                                  // Gets the user's network preference settings
                                                  SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
                                                  
                                                  // Retrieves a string value for the preferences. The second parameter
                                                  // is the default value to use if a preference value is not found.
                                                  sPref = sharedPrefs.getString("listPref", "Wi-Fi");
                                          
                                                  updateConnectedFlags(); 
                                                 
                                                  if(refreshDisplay){
                                                      loadPage();    
                                                  }
                                              }
                                              
                                              // Checks the network connection and sets the wifiConnected and mobileConnected
                                              // variables accordingly. 
                                              public void updateConnectedFlags() {
                                                  ConnectivityManager connMgr = (ConnectivityManager) 
                                                          getSystemService(Context.CONNECTIVITY_SERVICE);
                                                  
                                                  NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
                                                  if (activeInfo != null && activeInfo.isConnected()) {
                                                      wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
                                                      mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
                                                  } else {
                                                      wifiConnected = false;
                                                      mobileConnected = false;
                                                  }  
                                              }
                                                
                                              // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
                                              public void loadPage() {
                                                  if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
                                                          || ((sPref.equals(WIFI)) && (wifiConnected))) {
                                                      // AsyncTask subclass
                                                      new DownloadXmlTask().execute(URL);
                                                  } else {
                                                      showErrorPage();
                                                  }
                                              }
                                          ...
                                              
                                          }

                                          Detect Connection Changes

                                          The final piece of the puzzle is the BroadcastReceiver subclass, NetworkReceiver. When the device's network connection changes, NetworkReceiver intercepts the action CONNECTIVITY_ACTION, determines what the network connection status is, and sets the flags wifiConnected and mobileConnected to true/false accordingly. The upshot is that the next time the user returns to the app, the app will only download the latest feed and update the display if NetworkActivity.refreshDisplay is set to true.

                                          Setting up a BroadcastReceiver that gets called unnecessarily can be a drain on system resources. The sample application registers the BroadcastReceiver NetworkReceiver in onCreate(), and it unregisters it in onDestroy(). This is more lightweight than declaring a <receiver> in the manifest. When you declare a <receiver> in the manifest, it can wake up your app at any time, even if you haven't run it for weeks. By registering and unregistering NetworkReceiver within the main activity, you ensure that the app won't be woken up after the user leaves the app. If you do declare a <receiver> in the manifest and you know exactly where you need it, you can use setComponentEnabledSetting() to enable and disable it as appropriate.

                                          Here is NetworkReceiver:

                                          public class NetworkReceiver extends BroadcastReceiver {   
                                                
                                          @Override
                                          public void onReceive(Context context, Intent intent) {
                                              ConnectivityManager conn =  (ConnectivityManager)
                                                  context.getSystemService(Context.CONNECTIVITY_SERVICE);
                                              NetworkInfo networkInfo = conn.getActiveNetworkInfo();
                                                 
                                              // Checks the user prefs and the network connection. Based on the result, decides whether
                                              // to refresh the display or keep the current display.
                                              // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
                                              if (WIFI.equals(sPref) && networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                                                  // If device has its Wi-Fi connection, sets refreshDisplay
                                                  // to true. This causes the display to be refreshed when the user
                                                  // returns to the app.
                                                  refreshDisplay = true;
                                                  Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
                                          
                                              // If the setting is ANY network and there is a network connection
                                              // (which by process of elimination would be mobile), sets refreshDisplay to true.
                                              } else if (ANY.equals(sPref) && networkInfo != null) {
                                                  refreshDisplay = true;
                                                           
                                              // Otherwise, the app can't download content--either because there is no network
                                              // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there 
                                              // is no Wi-Fi connection.
                                              // Sets refreshDisplay to false.
                                              } else {
                                                  refreshDisplay = false;
                                                  Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
                                              }
                                          }
                                          This site uses cookies to store your preferences for site-specific language and display options.

                                          This class requires API level or higher

                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                          Parsing XML Data | Android Developers Skip to content

                                          Most visited

                                          Recently visited

                                          navigation

                                            Parsing XML Data

                                            Extensible Markup Language (XML) is a set of rules for encoding documents in machine-readable form. XML is a popular format for sharing data on the internet. Websites that frequently update their content, such as news sites or blogs, often provide an XML feed so that external programs can keep abreast of content changes. Uploading and parsing XML data is a common task for network-connected apps. This lesson explains how to parse XML documents and use their data.

                                            Choose a Parser

                                            We recommend XmlPullParser, which is an efficient and maintainable way to parse XML on Android. Historically Android has had two implementations of this interface:

                                            Either choice is fine. The example in this section uses ExpatPullParser, via Xml.newPullParser().

                                            Analyze the Feed

                                            The first step in parsing a feed is to decide which fields you're interested in. The parser extracts data for those fields and ignores the rest.

                                            Here is an excerpt from the feed that's being parsed in the sample app. Each post to StackOverflow.com appears in the feed as an entry tag that contains several nested tags:

                                            <?xml version="1.0" encoding="utf-8"?> 
                                            <feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" ...">     
                                            <title type="text">newest questions tagged android - Stack Overflow</title>
                                            ...
                                                <entry>
                                                ...
                                                </entry>
                                                <entry>
                                                    <id>http://stackoverflow.com/q/9439999</id>
                                                    <re:rank scheme="http://stackoverflow.com">0</re:rank>
                                                    <title type="text">Where is my data file?</title>
                                                    <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="android"/>
                                                    <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="file"/>
                                                    <author>
                                                        <name>cliff2310</name>
                                                        <uri>http://stackoverflow.com/users/1128925</uri>
                                                    </author>
                                                    <link rel="alternate" href="http://stackoverflow.com/questions/9439999/where-is-my-data-file" />
                                                    <published>2012-02-25T00:30:54Z</published>
                                                    <updated>2012-02-25T00:30:54Z</updated>
                                                    <summary type="html">
                                                        <p>I have an Application that requires a data file...</p>
                                            
                                                    </summary>
                                                </entry>
                                                <entry>
                                                ...
                                                </entry>
                                            ...
                                            </feed>

                                            The sample app extracts data for the entry tag and its nested tags title, link, and summary.

                                            Instantiate the Parser

                                            The next step is to instantiate a parser and kick off the parsing process. In this snippet, a parser is initialized to not process namespaces, and to use the provided InputStream as its input. It starts the parsing process with a call to nextTag() and invokes the readFeed() method, which extracts and processes the data the app is interested in:

                                            public class StackOverflowXmlParser {
                                                // We don't use namespaces
                                                private static final String ns = null;
                                               
                                                public List parse(InputStream in) throws XmlPullParserException, IOException {
                                                    try {
                                                        XmlPullParser parser = Xml.newPullParser();
                                                        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
                                                        parser.setInput(in, null);
                                                        parser.nextTag();
                                                        return readFeed(parser);
                                                    } finally {
                                                        in.close();
                                                    }
                                                }
                                             ... 
                                            }

                                            Read the Feed

                                            The readFeed() method does the actual work of processing the feed. It looks for elements tagged "entry" as a starting point for recursively processing the feed. If a tag isn't an entry tag, it skips it. Once the whole feed has been recursively processed, readFeed() returns a List containing the entries (including nested data members) it extracted from the feed. This List is then returned by the parser.

                                            private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
                                                List entries = new ArrayList();
                                            
                                                parser.require(XmlPullParser.START_TAG, ns, "feed");
                                                while (parser.next() != XmlPullParser.END_TAG) {
                                                    if (parser.getEventType() != XmlPullParser.START_TAG) {
                                                        continue;
                                                    }
                                                    String name = parser.getName();
                                                    // Starts by looking for the entry tag
                                                    if (name.equals("entry")) {
                                                        entries.add(readEntry(parser));
                                                    } else {
                                                        skip(parser);
                                                    }
                                                }  
                                                return entries;
                                            }

                                            Parse XML

                                            The steps for parsing an XML feed are as follows:

                                            1. As described in Analyze the Feed, identify the tags you want to include in your app. This example extracts data for the entry tag and its nested tags title, link, and summary.
                                            2. Create the following methods:

                                              • A "read" method for each tag you're interested in. For example, readEntry(), readTitle(), and so on. The parser reads tags from the input stream. When it encounters a tag named entry, title, link or summary, it calls the appropriate method for that tag. Otherwise, it skips the tag.
                                              • Methods to extract data for each different type of tag and to advance the parser to the next tag. For example:
                                                • For the title and summary tags, the parser calls readText(). This method extracts data for these tags by calling parser.getText().
                                                • For the link tag, the parser extracts data for links by first determining if the link is the kind it's interested in. Then it uses parser.getAttributeValue() to extract the link's value.
                                                • For the entry tag, the parser calls readEntry(). This method parses the entry's nested tags and returns an Entry object with the data members title, link, and summary.
                                              • A helper skip() method that's recursive. For more discussion of this topic, see Skip Tags You Don't Care About.

                                            This snippet shows how the parser parses entries, titles, links, and summaries.

                                            public static class Entry {
                                                public final String title;
                                                public final String link;
                                                public final String summary;
                                            
                                                private Entry(String title, String summary, String link) {
                                                    this.title = title;
                                                    this.summary = summary;
                                                    this.link = link;
                                                }
                                            }
                                              
                                            // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off
                                            // to their respective "read" methods for processing. Otherwise, skips the tag.
                                            private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
                                                parser.require(XmlPullParser.START_TAG, ns, "entry");
                                                String title = null;
                                                String summary = null;
                                                String link = null;
                                                while (parser.next() != XmlPullParser.END_TAG) {
                                                    if (parser.getEventType() != XmlPullParser.START_TAG) {
                                                        continue;
                                                    }
                                                    String name = parser.getName();
                                                    if (name.equals("title")) {
                                                        title = readTitle(parser);
                                                    } else if (name.equals("summary")) {
                                                        summary = readSummary(parser);
                                                    } else if (name.equals("link")) {
                                                        link = readLink(parser);
                                                    } else {
                                                        skip(parser);
                                                    }
                                                }
                                                return new Entry(title, summary, link);
                                            }
                                            
                                            // Processes title tags in the feed.
                                            private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
                                                parser.require(XmlPullParser.START_TAG, ns, "title");
                                                String title = readText(parser);
                                                parser.require(XmlPullParser.END_TAG, ns, "title");
                                                return title;
                                            }
                                              
                                            // Processes link tags in the feed.
                                            private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
                                                String link = "";
                                                parser.require(XmlPullParser.START_TAG, ns, "link");
                                                String tag = parser.getName();
                                                String relType = parser.getAttributeValue(null, "rel");  
                                                if (tag.equals("link")) {
                                                    if (relType.equals("alternate")){
                                                        link = parser.getAttributeValue(null, "href");
                                                        parser.nextTag();
                                                    } 
                                                }
                                                parser.require(XmlPullParser.END_TAG, ns, "link");
                                                return link;
                                            }
                                            
                                            // Processes summary tags in the feed.
                                            private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
                                                parser.require(XmlPullParser.START_TAG, ns, "summary");
                                                String summary = readText(parser);
                                                parser.require(XmlPullParser.END_TAG, ns, "summary");
                                                return summary;
                                            }
                                            
                                            // For the tags title and summary, extracts their text values.
                                            private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
                                                String result = "";
                                                if (parser.next() == XmlPullParser.TEXT) {
                                                    result = parser.getText();
                                                    parser.nextTag();
                                                }
                                                return result;
                                            }
                                              ...
                                            }

                                            Skip Tags You Don't Care About

                                            One of the steps in the XML parsing described above is for the parser to skip tags it's not interested in. Here is the parser's skip() method:

                                            private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
                                                if (parser.getEventType() != XmlPullParser.START_TAG) {
                                                    throw new IllegalStateException();
                                                }
                                                int depth = 1;
                                                while (depth != 0) {
                                                    switch (parser.next()) {
                                                    case XmlPullParser.END_TAG:
                                                        depth--;
                                                        break;
                                                    case XmlPullParser.START_TAG:
                                                        depth++;
                                                        break;
                                                    }
                                                }
                                             }
                                            

                                            This is how it works:

                                            • It throws an exception if the current event isn't a START_TAG.
                                            • It consumes the START_TAG, and all events up to and including the matching END_TAG.
                                            • To make sure that it stops at the correct END_TAG and not at the first tag it encounters after the original START_TAG, it keeps track of the nesting depth.

                                            Thus if the current element has nested elements, the value of depth won't be 0 until the parser has consumed all events between the original START_TAG and its matching END_TAG. For example, consider how the parser skips the <author> element, which has 2 nested elements, <name> and <uri>:

                                            • The first time through the while loop, the next tag the parser encounters after <author> is the START_TAG for <name>. The value for depth is incremented to 2.
                                            • The second time through the while loop, the next tag the parser encounters is the END_TAG </name>. The value for depth is decremented to 1.
                                            • The third time through the while loop, the next tag the parser encounters is the START_TAG <uri>. The value for depth is incremented to 2.
                                            • The fourth time through the while loop, the next tag the parser encounters is the END_TAG </uri>. The value for depth is decremented to 1.
                                            • The fifth time and final time through the while loop, the next tag the parser encounters is the END_TAG </author>. The value for depth is decremented to 0, indicating that the <author> element has been successfully skipped.

                                            Consume XML Data

                                            The example application fetches and parses the XML feed within an AsyncTask. This takes the processing off the main UI thread. When processing is complete, the app updates the UI in the main activity (NetworkActivity).

                                            In the excerpt shown below, the loadPage() method does the following:

                                            • Initializes a string variable with the URL for the XML feed.
                                            • If the user's settings and the network connection allow it, invokes new DownloadXmlTask().execute(url). This instantiates a new DownloadXmlTask object (AsyncTask subclass) and runs its execute() method, which downloads and parses the feed and returns a string result to be displayed in the UI.
                                            public class NetworkActivity extends Activity {
                                                public static final String WIFI = "Wi-Fi";
                                                public static final String ANY = "Any";
                                                private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
                                               
                                                // Whether there is a Wi-Fi connection.
                                                private static boolean wifiConnected = false; 
                                                // Whether there is a mobile connection.
                                                private static boolean mobileConnected = false;
                                                // Whether the display should be refreshed.
                                                public static boolean refreshDisplay = true; 
                                                public static String sPref = null;
                                            
                                                ...
                                                  
                                                // Uses AsyncTask to download the XML feed from stackoverflow.com.
                                                public void loadPage() {  
                                                  
                                                    if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) {
                                                        new DownloadXmlTask().execute(URL);
                                                    }
                                                    else if ((sPref.equals(WIFI)) && (wifiConnected)) {
                                                        new DownloadXmlTask().execute(URL);
                                                    } else {
                                                        // show error
                                                    }  
                                                }

                                            The AsyncTask subclass shown below, DownloadXmlTask, implements the following AsyncTask methods:

                                            • doInBackground() executes the method loadXmlFromNetwork(). It passes the feed URL as a parameter. The method loadXmlFromNetwork() fetches and processes the feed. When it finishes, it passes back a result string.
                                            • onPostExecute() takes the returned string and displays it in the UI.
                                            // Implementation of AsyncTask used to download XML feed from stackoverflow.com.
                                            private class DownloadXmlTask extends AsyncTask<String, Void, String> {
                                                @Override
                                                protected String doInBackground(String... urls) {
                                                    try {
                                                        return loadXmlFromNetwork(urls[0]);
                                                    } catch (IOException e) {
                                                        return getResources().getString(R.string.connection_error);
                                                    } catch (XmlPullParserException e) {
                                                        return getResources().getString(R.string.xml_error);
                                                    }
                                                }
                                            
                                                @Override
                                                protected void onPostExecute(String result) {  
                                                    setContentView(R.layout.main);
                                                    // Displays the HTML string in the UI via a WebView
                                                    WebView myWebView = (WebView) findViewById(R.id.webview);
                                                    myWebView.loadData(result, "text/html", null);
                                                }
                                            }

                                            Below is the method loadXmlFromNetwork() that is invoked from DownloadXmlTask. It does the following:

                                            1. Instantiates a StackOverflowXmlParser. It also creates variables for a List of Entry objects (entries), and title, url, and summary, to hold the values extracted from the XML feed for those fields.
                                            2. Calls downloadUrl(), which fetches the feed and returns it as an InputStream.
                                            3. Uses StackOverflowXmlParser to parse the InputStream. StackOverflowXmlParser populates a List of entries with data from the feed.
                                            4. Processes the entries List, and combines the feed data with HTML markup.
                                            5. Returns an HTML string that is displayed in the main activity UI by the AsyncTask method onPostExecute().
                                            // Uploads XML from stackoverflow.com, parses it, and combines it with
                                            // HTML markup. Returns HTML string.
                                            private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
                                                InputStream stream = null;
                                                // Instantiate the parser
                                                StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
                                                List<Entry> entries = null;
                                                String title = null;
                                                String url = null;
                                                String summary = null;
                                                Calendar rightNow = Calendar.getInstance(); 
                                                DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");
                                                    
                                                // Checks whether the user set the preference to include summary text
                                                SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
                                                boolean pref = sharedPrefs.getBoolean("summaryPref", false);
                                                    
                                                StringBuilder htmlString = new StringBuilder();
                                                htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
                                                htmlString.append("<em>" + getResources().getString(R.string.updated) + " " + 
                                                        formatter.format(rightNow.getTime()) + "</em>");
                                                    
                                                try {
                                                    stream = downloadUrl(urlString);        
                                                    entries = stackOverflowXmlParser.parse(stream);
                                                // Makes sure that the InputStream is closed after the app is
                                                // finished using it.
                                                } finally {
                                                    if (stream != null) {
                                                        stream.close();
                                                    } 
                                                 }
                                                
                                                // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
                                                // Each Entry object represents a single post in the XML feed.
                                                // This section processes the entries list to combine each entry with HTML markup.
                                                // Each entry is displayed in the UI as a link that optionally includes
                                                // a text summary.
                                                for (Entry entry : entries) {       
                                                    htmlString.append("<p><a href='");
                                                    htmlString.append(entry.link);
                                                    htmlString.append("'>" + entry.title + "</a></p>");
                                                    // If the user set the preference to include summary text,
                                                    // adds it to the display.
                                                    if (pref) {
                                                        htmlString.append(entry.summary);
                                                    }
                                                }
                                                return htmlString.toString();
                                            }
                                            
                                            // Given a string representation of a URL, sets up a connection and gets
                                            // an input stream.
                                            private InputStream downloadUrl(String urlString) throws IOException {
                                                URL url = new URL(urlString);
                                                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                                                conn.setReadTimeout(10000 /* milliseconds */);
                                                conn.setConnectTimeout(15000 /* milliseconds */);
                                                conn.setRequestMethod("GET");
                                                conn.setDoInput(true);
                                                // Starts the query
                                                conn.connect();
                                                return conn.getInputStream();
                                            }
                                            This site uses cookies to store your preferences for site-specific language and display options.

                                            This class requires API level or higher

                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                            Transferring Data Without Draining the Battery | Android Developers Skip to content

                                            Most visited

                                            Recently visited

                                            navigation

                                              Transferring Data Without Draining the Battery

                                              Dependencies and prerequisites

                                              • Android 2.0 (API Level 5) or higher

                                              You should also read

                                              In this class you will learn to minimize the battery life impact of downloads and network connections, particularly in relation to the wireless radio.

                                              This class demonstrates the best practices for scheduling and executing downloads using techniques such as caching, polling, and prefetching. You will learn how the power-use profile of the wireless radio can affect your choices on when, what, and how to transfer data in order to minimize impact on battery life.

                                              Lessons

                                              Optimizing Downloads for Efficient Network Access
                                              This lesson introduces the wireless radio state machine, explains how your app’s connectivity model interacts with it, and how you can minimize your data connection and use prefetching and bundling to minimize the battery drain associated with your data transfers.
                                              Minimizing the Effect of Regular Updates
                                              This lesson will examine how your refresh frequency can be varied to best mitigate the effect of background updates on the underlying wireless radio state machine.
                                              Redundant Downloads are Redundant
                                              The most fundamental way to reduce your downloads is to download only what you need. This lesson introduces some best practices to eliminate redundant downloads.
                                              Modifying your Download Patterns Based on the Connectivity Type
                                              When it comes to impact on battery life, not all connection types are created equal. Not only does the Wi-Fi radio use significantly less battery than its wireless radio counterparts, but the radios used in different wireless radio technologies have different battery implications.
                                              This site uses cookies to store your preferences for site-specific language and display options.

                                              This class requires API level or higher

                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                              Optimizing Downloads for Efficient Network Access | Android Developers Skip to content

                                              Most visited

                                              Recently visited

                                              navigation

                                                Optimizing Downloads for Efficient Network Access

                                                Using the wireless radio to transfer data is potentially one of your app's most significant sources of battery drain. To minimize the battery drain associated with network activity, it's critical that you understand how your connectivity model will affect the underlying radio hardware.

                                                This lesson introduces the wireless radio state machine and explains how your app's connectivity model interacts with it. It goes on to propose ways to minimize your data connections, use prefetching, and bundle your transfers in order to minimize the battery drain associated with your data transfers.

                                                The Radio State Machine

                                                A fully active wireless radio consumes significant power, so it transitions between different energy states in order to conserve power when not in use, while attempting to minimize latency associated with "powering up" the radio when it's required.

                                                The state machine for a typical 3G network radio consists of three energy states:

                                                1. Full power: Used when a connection is active, allowing the device to transfer data at its highest possible rate.
                                                2. Low power: An intermediate state that uses around 50% of the battery power at the full state.
                                                3. Standby: The minimal energy state during which no network connection is active or required.

                                                While the low and idle states drain significantly less battery, they also introduce significant latency to network requests. Returning to full power from the low state takes around 1.5 seconds, while moving from idle to full can take over 2 seconds.

                                                To minimize latency, the state machine uses a delay to postpone the transition to lower energy states. Figure 1 uses AT&T's timings for a typical 3G radio.

                                                Figure 1. Typical 3G wireless radio state machine.

                                                The radio state machine on each device, particularly the associated transition delay ("tail time") and startup latency, will vary based on the wireless radio technology employed (2G, 3G, LTE, etc.) and is defined and configured by the carrier network over which the device is operating.

                                                This lesson describes a representative state machine for a typical 3G wireless radio, based on data provided by AT&T. However, the general principles and resulting best practices are applicable for all wireless radio implementations.

                                                This approach is particularly effective for typical web browsing as it prevents unwelcome latency while users browse the web. The relatively low tail-time also ensures that once a browsing session has finished, the radio can move to a lower energy state.

                                                Unfortunately, this approach can lead to inefficient apps on modern smartphone OSs like Android, where apps run both in the foreground (where latency is important) and in the background (where battery life should be prioritized).

                                                How Apps Impact the Radio State Machine

                                                Every time you create a new network connection, the radio transitions to the full power state. In the case of the typical 3G radio state machine described above, it will remain at full power for the duration of your transfer—plus an additional 5 seconds of tail time—followed by 12 seconds at the low energy state. So for a typical 3G device, every data transfer session will cause the radio to draw energy for almost 20 seconds.

                                                In practice, this means an app that transfers unbundled data for 1 second every 18 seconds will keep the wireless radio perpetually active, moving it back to high power just as it was about to become idle. As a result, every minute it will consume battery at the high power state for 18 seconds, and at the low power state for the remaining 42 seconds.

                                                By comparison, the same app that bundles transfers of 3 seconds of every minute will keep the radio in the high power state for only 8 seconds, and will keep it in the low power state for only an additional 12 seconds.

                                                The second example allows the radio to be idle for an additional 40 second every minute, resulting in a massive reduction in battery consumption.

                                                Figure 2. Relative wireless radio power use for bundled versus unbundled transfers.

                                                Prefetch Data

                                                Prefetching data is an effective way to reduce the number of independent data transfer sessions. Prefetching allows you to download all the data you are likely to need for a given time period in a single burst, over a single connection, at full capacity.

                                                By front loading your transfers, you reduce the number of radio activations required to download the data. As a result you not only conserve battery life, but also improve the latency, lower the required bandwidth, and reduce download times.

                                                Prefetching also provides an improved user experience by minimizing in-app latency caused by waiting for downloads to complete before performing an action or viewing data.

                                                However, used too aggressively, prefetching introduces the risk of increasing battery drain and bandwidth use—as well as download quota—by downloading data that isn't used. It's also important to ensure that prefetching doesn't delay application startup while the app waits for the prefetch to complete. In practical terms that might mean processing data progressively, or initiating consecutive transfers prioritized such that the data required for application startup is downloaded and processed first.

                                                How aggressively you prefetch depends on the size of the data being downloaded and the likelihood of it being used. As a rough guide, based on the state machine described above, for data that has a 50% chance of being used within the current user session, you can typically prefetch for around 6 seconds (approximately 1-2 Mb) before the potential cost of downloading unused data matches the potential savings of not downloading that data to begin with.

                                                Generally speaking, it's good practice to prefetch data such that you will only need to initiate another download every 2 to 5 minutes, and in the order of 1 to 5 megabytes.

                                                Following this principle, large downloads—such as video files—should be downloaded in chunks at regular intervals (every 2 to 5 minutes), effectively prefetching only the video data likely to be viewed in the next few minutes.

                                                Note that further downloads should be bundled, as described in the next section, Batch Transfers and Connections, and that these approximations will vary based on the connection type and speed, as discussed in Modify your Download Patterns Based on the Connectivity Type.

                                                Let's look at some practical examples:

                                                A music player

                                                You could choose to prefetch an entire album, however should the user stop listening after the first song, you've wasted a significant amount of bandwidth and battery life.

                                                A better approach would be to maintain a buffer of one song in addition to the one being played. For streaming music, rather than maintaining a continuous stream that keeps the radio active at all times, consider using HTTP live streaming to transmit the audio stream in bursts, simulating the prefetching approach described above.

                                                A news reader

                                                Many news apps attempt to reduce bandwidth by downloading headlines only after a category has been selected, full articles only when the user wants to read them, and thumbnails just as they scroll into view.

                                                Using this approach, the radio will be forced to remain active for the majority of users' news-reading session as they scroll headlines, change categories, and read articles. Not only that, but the constant switching between energy states will result in significant latency when switching categories or reading articles.

                                                A better approach would be to prefetch a reasonable amount of data at startup, beginning with the first set of news headlines and thumbnails—ensuring a low latency startup time—and continuing with the remaining headlines and thumbnails, as well as the article text for each article available from at least the primary headline list.

                                                Another alternative is to prefetch every headline, thumbnail, article text, and possibly even full article pictures—typically in the background on a predetermined schedule. This approach risks spending significant bandwidth and battery life downloading content that's never used, so it should be implemented with caution.

                                                One solution is to schedule the full download to occur only when connected to Wi-Fi, and possibly only when the device is charging. This is investigated in more detail in Modify your Download Patterns Based on the Connectivity Type.

                                                Batch Transfers and Connections

                                                Every time you initiate a connection—irrespective of the size of the associated data transfer—you potentially cause the radio to draw power for nearly 20 seconds when using a typical 3G wireless radio.

                                                An app that pings the server every 20 seconds, just to acknowledge that the app is running and visible to the user, will keep the radio powered on indefinitely, resulting in a significant battery cost for almost no actual data transfer.

                                                With that in mind it's important to bundle your data transfers and create a pending transfer queue. Done correctly, you can effectively phase-shift transfers that are due to occur within a similar time window, to make them all happen simultaneously—ensuring that the radio draws power for as short a duration as possible.

                                                The underlying philosophy of this approach is to transfer as much data as possible during each transfer session in an effort to limit the number of sessions you require.

                                                That means you should batch your transfers by queuing delay tolerant transfers, and preempting scheduled updates and prefetches, so that they are all executed when time-sensitive transfers are required. Similarly, your scheduled updates and regular prefetching should initiate the execution of your pending transfer queue.

                                                For a practical example, let's return to the earlier examples from Prefetch Data.

                                                Take a news application that uses the prefetching routine described above. The news reader collects analytics information to understand the reading patterns of its users and to rank the most popular stories. To keep the news fresh, it checks for updates every hour. To conserve bandwidth, rather than download full photos for each article, it prefetches only thumbnails and downloads the full photos when they are selected.

                                                In this example, all the analytics information collected within the app should be bundled together and queued for download, rather than being transmitted as it's collected. The resulting bundle should be transferred when either a full-sized photo is being downloaded, or when an hourly update is being performed.

                                                Any time-sensitive or on-demand transfer—such as downloading a full-sized image—should preempt regularly scheduled updates. The planned update should be executed at the same time as the on-demand transfer, with the next update scheduled to occur after the set interval. This approach mitigates the cost of performing a regular update by piggy-backing on the necessary time-sensitive photo download.

                                                Reduce Connections

                                                It's generally more efficient to reuse existing network connections than to initiate new ones. Reusing connections also allows the network to more intelligently react to congestion and related network data issues.

                                                Rather than creating multiple simultaneous connections to download data, or chaining multiple consecutive GET requests, where possible you should bundle those requests into a single GET.

                                                For example, it would be more efficient to make a single request for every news article to be returned in a single request / response than to make multiple queries for several news categories. The wireless radio needs to become active in order to transmit the termination / termination acknowledgement packets associated with server and client timeout, so it's also good practice to close your connections when they aren't in use, rather than waiting for these timeouts.

                                                That said, closing a connection too early can prevent it from being reused, which then requires additional overhead for establishing a new connection. A useful compromise is not to close the connection immediately, but to still close it before the inherent timeout expires.

                                                Use the DDMS Network Traffic Tool to Identify Areas of Concern

                                                The Android DDMS (Dalvik Debug Monitor Server) includes a Detailed Network Usage tab that makes it possible to track when your application is making network requests. Using this tool, you can monitor how and when your app transfers data and optimize the underlying code appropriately.

                                                Figure 3 shows a pattern of transferring small amounts of data roughly 15 seconds apart, suggesting that efficiency could be dramatically improved by prefetching each request or bundling the uploads.

                                                Figure 3. Tracking network usage with DDMS.

                                                By monitoring the frequency of your data transfers, and the amount of data transferred during each connection, you can identify areas of your application that can be made more battery-efficient. Generally, you will be looking for short spikes that can be delayed, or that should cause a later transfer to be preempted.

                                                To better identify the cause of transfer spikes, the Traffic Stats API allows you to tag the data transfers occurring within a thread using the TrafficStats.setThreadStatsTag() method, followed by manually tagging (and untagging) individual sockets using tagSocket() and untagSocket(). For example:

                                                TrafficStats.setThreadStatsTag(0xF00D);
                                                TrafficStats.tagSocket(outputSocket);
                                                // Transfer data using socket
                                                TrafficStats.untagSocket(outputSocket);

                                                The Apache HttpClient and URLConnection libraries automatically tag sockets based on the current getThreadStatsTag() value. These libraries also tag and untag sockets when recycled through keep-alive pools.

                                                TrafficStats.setThreadStatsTag(0xF00D);
                                                try {
                                                  // Make network request using HttpClient.execute()
                                                } finally {
                                                  TrafficStats.clearThreadStatsTag();
                                                }

                                                Socket tagging is supported in Android 4.0, but real-time stats will only be displayed on devices running Android 4.0.3 or higher.

                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                This class requires API level or higher

                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                Minimizing the Effect of Regular Updates | Android Developers Skip to content

                                                Most visited

                                                Recently visited

                                                navigation

                                                  Minimizing the Effect of Regular Updates

                                                  The optimal frequency of regular updates will vary based on device state, network connectivity, user behavior, and explicit user preferences.

                                                  Optimizing Battery Life discusses how to build battery-efficient apps that modify their refresh frequency based on the state of the host device. That includes disabling background service updates when you lose connectivity and reducing the rate of updates when the battery level is low.

                                                  This lesson will examine how your refresh frequency can be varied to best mitigate the effect of background updates on the underlying wireless radio state machine.

                                                  Use Google Cloud Messaging as an Alternative to Polling

                                                  Every time your app polls your server to check if an update is required, you activate the wireless radio, drawing power unnecessarily, for up to 20 seconds on a typical 3G connection.

                                                  Google Cloud Messaging for Android (GCM) is a lightweight mechanism used to transmit data from a server to a particular app instance. Using GCM, your server can notify your app running on a particular device that there is new data available for it.

                                                  Compared to polling, where your app must regularly ping the server to query for new data, this event-driven model allows your app to create a new connection only when it knows there is data to download.

                                                  The result is a reduction in unnecessary connections, and a reduced latency for updated data within your application.

                                                  GCM is implemented using a persistent TCP/IP connection. While it's possible to implement your own push service, it's best practice to use GCM. This minimizes the number of persistent connections and allows the platform to optimize bandwidth and minimize the associated impact on battery life.

                                                  Optimize Polling with Inexact Repeating Alarms and Exponential Backoffs

                                                  Where polling is required, it's good practice to set the default data refresh frequency of your app as low as possible without detracting from the user experience.

                                                  A simple approach is to offer preferences to allow users to explicitly set their required update rate, allowing them to define their own balance between data freshness and battery life.

                                                  When scheduling updates, use inexact repeating alarms that allow the system to "phase shift" the exact moment each alarm triggers.

                                                  int alarmType = AlarmManager.ELAPSED_REALTIME;
                                                  long interval = AlarmManager.INTERVAL_HOUR;
                                                  long start = System.currentTimeMillis() + interval;
                                                  
                                                  alarmManager.setInexactRepeating(alarmType, start, interval, pi);

                                                  If several alarms are scheduled to trigger at similar times, this phase-shifting will cause them to be triggered simultaneously, allowing each update to piggyback on top of a single active radio state change.

                                                  Wherever possible, set your alarm type to ELAPSED_REALTIME or RTC rather than to their _WAKEUP equivalents. This further reduces battery impact by waiting until the phone is no longer in standby mode before the alarm triggers.

                                                  You can further reduce the impact of these scheduled alarms by opportunistically reducing their frequency based on how recently your app was used.

                                                  One approach is to implement an exponential back-off pattern to reduce the frequency of your updates (and / or the degree of prefetching you perform) if the app hasn't been used since the previous update. It's often useful to assert a minimum update frequency and to reset the frequency whenever the app is used, for example:

                                                  SharedPreferences sp = 
                                                    context.getSharedPreferences(PREFS, Context.MODE_WORLD_READABLE);
                                                  
                                                  boolean appUsed = sp.getBoolean(PREFS_APPUSED, false);
                                                  long updateInterval = sp.getLong(PREFS_INTERVAL, DEFAULT_REFRESH_INTERVAL);
                                                  
                                                  if (!appUsed)
                                                    if ((updateInterval *= 2) > MAX_REFRESH_INTERVAL)  
                                                      updateInterval = MAX_REFRESH_INTERVAL;
                                                  
                                                  Editor spEdit = sp.edit();
                                                  spEdit.putBoolean(PREFS_APPUSED, false);
                                                  spEdit.putLong(PREFS_INTERVAL, updateInterval);
                                                  spEdit.apply();
                                                  
                                                  rescheduleUpdates(updateInterval);
                                                  executeUpdateOrPrefetch();

                                                  You can use a similar exponential back-off pattern to reduce the effect of failed connections and download errors.

                                                  The cost of initiating a network connection is the same whether you are able to contact your server and download data or not. For time-sensitive transfers where successful completion is important, an exponential back-off algorithm can be used to reduce the frequency of retries in order to minimize the associated battery impact, for example:

                                                  private void retryIn(long interval) {
                                                    boolean success = attemptTransfer();
                                                      
                                                    if (!success) {
                                                      retryIn(interval*2 < MAX_RETRY_INTERVAL ? 
                                                              interval*2 : MAX_RETRY_INTERVAL);      
                                                    }
                                                  }

                                                  Alternatively, for transfers that are failure tolerant (such as regular updates), you can simply ignore failed connection and transfer attempts.

                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                  This class requires API level or higher

                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                  Redundant Downloads are Redundant | Android Developers Skip to content

                                                  Most visited

                                                  Recently visited

                                                  navigation

                                                    Redundant Downloads are Redundant

                                                    The most fundamental way to reduce your downloads is to download only what you need. In terms of data, that means implementing REST APIs that allow you to specify query criteria that limit the returned data by using parameters such as the time of your last update.

                                                    Similarly, when downloading images, it's good practice to reduce the size of the images server-side, rather than downloading full-sized images that are reduced on the client.

                                                    Cache Files Locally

                                                    Another important technique is to avoid downloading duplicate data. You can do this by aggressive caching. Always cache static resources, including on-demand downloads such as full size images, for as long as reasonably possible. On-demand resources should be stored separately to enable you to regularly flush your on-demand cache to manage its size.

                                                    To ensure that your caching doesn't result in your app displaying stale data, be sure to extract the time at which the requested content was last updated, and when it expires, from within the HTTP response headers. This will allow you to determine when the associated content should be refreshed.

                                                    long currentTime = System.currentTimeMillis();
                                                    
                                                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                                                    
                                                    long expires = conn.getHeaderFieldDate("Expires", currentTime);
                                                    long lastModified = conn.getHeaderFieldDate("Last-Modified", currentTime);
                                                    
                                                    setDataExpirationDate(expires);
                                                    
                                                    if (lastModified < lastUpdateTime) {
                                                      // Skip update
                                                    } else {
                                                      // Parse update
                                                    }

                                                    Using this approach, you can also effectively cache dynamic content while ensuring it doesn't result in your application displaying stale information.

                                                    You can cache non-sensitive data in the unmanaged external cache directory:

                                                    Context.getExternalCacheDir();

                                                    Alternatively, you can use the managed / secure application cache. Note that this internal cache may be flushed when the system is running low on available storage.

                                                    Context.getCacheDir();

                                                    Files stored in either cache location will be erased when the application is uninstalled.

                                                    Use the HttpURLConnection Response Cache

                                                    Android 4.0 added a response cache to HttpURLConnection. You can enable HTTP response caching on supported devices using reflection as follows:

                                                    private void enableHttpResponseCache() {
                                                      try {
                                                        long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
                                                        File httpCacheDir = new File(getCacheDir(), "http");
                                                        Class.forName("android.net.http.HttpResponseCache")
                                                             .getMethod("install", File.class, long.class)
                                                             .invoke(null, httpCacheDir, httpCacheSize);
                                                      } catch (Exception httpResponseCacheNotAvailable) {
                                                        Log.d(TAG, "HTTP response cache is unavailable.");
                                                      }
                                                    }

                                                    This sample code will turn on the response cache on Android 4.0+ devices without affecting earlier releases.

                                                    With the cache installed, fully cached HTTP requests can be served directly from local storage, eliminating the need to open a network connection. Conditionally cached responses can validate their freshness from the server, eliminating the bandwidth cost associated with the download.

                                                    Uncached responses get stored in the response cache for future requests.

                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                    This class requires API level or higher

                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                    Modifying your Download Patterns Based on the Connectivity Type | Android Developers Skip to content

                                                    Most visited

                                                    Recently visited

                                                    navigation

                                                      Modifying your Download Patterns Based on the Connectivity Type

                                                      When it comes to impact on battery life, not all connection types are created equal. Not only does the Wi-Fi radio use significantly less battery than its wireless radio counterparts, but the radios used in different wireless radio technologies have different battery implications.

                                                      Use Wi-Fi

                                                      In most cases a Wi-Fi radio will offer greater bandwidth at a significantly lower battery cost. As a result, you should endeavor to perform data transfers when connected over Wi-Fi whenever possible.

                                                      You can use a broadcast receiver to listen for connectivity changes that indicate when a Wi-Fi connection has been established to execute significant downloads, preempt scheduled updates, and potentially even temporarily increase the frequency of regular updates as described in Optimizing Battery Life lesson Determining and Monitoring the Connectivity Status.

                                                      Use Greater Bandwidth to Download More Data Less Often

                                                      When connected over a wireless radio, higher bandwidth generally comes at the price of higher battery cost. Meaning that LTE typically consumes more energy than 3G, which is in turn more expensive than 2G.

                                                      This means that while the underlying radio state machine varies based on the radio technology, generally speaking the relative battery impact of the state change tail-time is greater for higher bandwidth radios.

                                                      At the same time, the higher bandwidth means you can prefetch more aggressively, downloading more data over the same time. Perhaps less intuitively, because the tail-time battery cost is relatively higher, it's also more efficient to keep the radio active for longer periods during each transfer session to reduce the frequency of updates.

                                                      For example, if an LTE radio is has double the bandwidth and double the energy cost of 3G, you should download 4 times as much data during each session—or potentially as much as 10mb. When downloading this much data, it's important to consider the effect of your prefetching on the available local storage and flush your prefetch cache regularly.

                                                      You can use the connectivity manager to determine the active wireless radio, and modify your prefetching routines accordingly:

                                                      ConnectivityManager cm =
                                                       (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
                                                      
                                                      TelephonyManager tm =
                                                        (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
                                                        
                                                      NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
                                                       
                                                      int PrefetchCacheSize = DEFAULT_PREFETCH_CACHE;
                                                       
                                                      switch (activeNetwork.getType()) {
                                                        case (ConnectivityManager.TYPE_WIFI): 
                                                          PrefetchCacheSize = MAX_PREFETCH_CACHE; break;
                                                        case (ConnectivityManager.TYPE_MOBILE): {
                                                          switch (tm.getNetworkType()) {
                                                            case (TelephonyManager.NETWORK_TYPE_LTE | 
                                                                  TelephonyManager.NETWORK_TYPE_HSPAP): 
                                                              PrefetchCacheSize *= 4;
                                                              break;
                                                            case (TelephonyManager.NETWORK_TYPE_EDGE | 
                                                                  TelephonyManager.NETWORK_TYPE_GPRS): 
                                                              PrefetchCacheSize /= 2;
                                                              break;
                                                            default: break;
                                                          }
                                                          break;
                                                        }
                                                        default: break;
                                                      }
                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                      This class requires API level or higher

                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                      Backing up App Data to the Cloud | Android Developers Skip to content

                                                      Most visited

                                                      Recently visited

                                                      navigation

                                                        Backing up App Data to the Cloud

                                                        Dependencies and prerequisites

                                                        • Android 2.2 (API level 8) and higher

                                                        Users often invest significant time and effort creating data and setting preferences within apps. Preserving that data for users if they replace a broken device or upgrade to a new one is an important part of ensuring a great user experience.

                                                        This class covers techniques for backing up data to the cloud so that users can restore their data when recovering from a data loss (such as a factory reset) or installing your application on a new device.

                                                        It is important to note that the API for cloud backup changed with the release of Android 6.0 (API level 23). For your app to support backup both on devices running Android 6.0, and those running Android 5.1 (API level 22) and lower, you must implement both techniques that this class explains.

                                                        Lessons

                                                        Configuring Auto Backup for Apps
                                                        This lesson applies to Android 6.0 (API level 23) and higher. Learn how to accomplish seamless app data backup and restore with zero additional lines of application code.
                                                        Using the Backup API
                                                        This lesson applies to Android 5.1 (API level 22) and lower. Learn how to integrate the Backup API into your Android app, so all of that app's user data, such as preferences, notes, and high scores, updates seamlessly across all devices linked to that Google account.
                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                        This class requires API level or higher

                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                        Transferring Data Using Sync Adapters | Android Developers Skip to content

                                                        Most visited

                                                        Recently visited

                                                        navigation

                                                          Transferring Data Using Sync Adapters

                                                          Dependencies and prerequisites

                                                          • Android 2.1 (API Level 7) or higher

                                                          You should also read

                                                          Try it out

                                                          Download the sample

                                                          BasicSyncAdapter.zip

                                                          Synchronizing data between an Android device and web servers can make your application significantly more useful and compelling for your users. For example, transferring data to a web server makes a useful backup, and transferring data from a server makes it available to the user even when the device is offline. In some cases, users may find it easier to enter and edit their data in a web interface and then have that data available on their device, or they may want to collect data over time and then upload it to a central storage area.

                                                          Although you can design your own system for doing data transfers in your app, you should consider using Android's sync adapter framework. This framework helps manage and automate data transfers, and coordinates synchronization operations across different apps. When you use this framework, you can take advantage of several features that aren't available to data transfer schemes you design yourself:

                                                          Plug-in architecture
                                                          Allows you to add data transfer code to the system in the form of callable components.
                                                          Automated execution
                                                          Allows you to automate data transfer based on a variety of criteria, including data changes, elapsed time, or time of day. In addition, the system adds transfers that are unable to run to a queue, and runs them when possible.
                                                          Automated network checking
                                                          The system only runs your data transfer when the device has network connectivity.
                                                          Improved battery performance
                                                          Allows you to centralize all of your app's data transfer tasks in one place, so that they all run at the same time. Your data transfer is also scheduled in conjunction with data transfers from other apps. These factors reduce the number of times the system has to switch on the network, which reduces battery usage.
                                                          Account management and authentication
                                                          If your app requires user credentials or server login, you can optionally integrate account management and authentication into your data transfer.

                                                          This class shows you how to create a sync adapter and the bound Service that wraps it, how to provide the other components that help you plug the sync adapter into the framework, and how to run the sync adapter to run in various ways.

                                                          Note: Sync adapters run asynchronously, so you should use them with the expectation that they transfer data regularly and efficiently, but not instantaneously. If you need to do real-time data transfer, you should do it in an AsyncTask or an IntentService.

                                                          Lessons

                                                          Creating a Stub Authenticator
                                                          Learn how to add an account-handling component that the sync adapter framework expects to be part of your app. This lesson shows you how to create a stub authentication component for simplicity.
                                                          Creating a Stub Content Provider
                                                          Learn how to add a content provider component that the sync adapter framework expects to be part of your app. This lesson assumes that your app doesn't use a content provider, so it shows you how to add a stub component. If you have a content provider already in your app, you can skip this lesson.
                                                          Creating a Sync Adapter
                                                          Learn how to encapsulate your data transfer code in a component that the sync adapter framework can run automatically.
                                                          Running a Sync Adapter
                                                          Learn how to trigger and schedule data transfers using the sync adapter framework.
                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                          This class requires API level or higher

                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                          Creating a Stub Authenticator | Android Developers Skip to content

                                                          Most visited

                                                          Recently visited

                                                          navigation

                                                            Creating a Stub Authenticator

                                                            The sync adapter framework assumes that your sync adapter transfers data between device storage associated with an account and server storage that requires login access. For this reason, the framework expects you to provide a component called an authenticator as part of your sync adapter. This component plugs into the Android accounts and authentication framework and provides a standard interface for handling user credentials such as login information.

                                                            Even if your app doesn't use accounts, you still need to provide an authenticator component. If you don't use accounts or server login, the information handled by the authenticator is ignored, so you can provide an authenticator component that contains stub method implementations. You also need to provide a bound Service that allows the sync adapter framework to call the authenticator's methods.

                                                            This lesson shows you how to define all the parts of a stub authenticator that you need to satisfy the requirements of the sync adapter framework. If you need to provide a real authenticator that handles user accounts, read the reference documentation for AbstractAccountAuthenticator.

                                                            Add a Stub Authenticator Component

                                                            To add a stub authenticator component to your app, create a class that extends AbstractAccountAuthenticator, and then stub out the required methods, either by returning null or by throwing an exception.

                                                            The following snippet shows an example of a stub authenticator class:

                                                            /*
                                                             * Implement AbstractAccountAuthenticator and stub out all
                                                             * of its methods
                                                             */
                                                            public class Authenticator extends AbstractAccountAuthenticator {
                                                                // Simple constructor
                                                                public Authenticator(Context context) {
                                                                    super(context);
                                                                }
                                                                // Editing properties is not supported
                                                                @Override
                                                                public Bundle editProperties(
                                                                        AccountAuthenticatorResponse r, String s) {
                                                                    throw new UnsupportedOperationException();
                                                                }
                                                                // Don't add additional accounts
                                                                @Override
                                                                public Bundle addAccount(
                                                                        AccountAuthenticatorResponse r,
                                                                        String s,
                                                                        String s2,
                                                                        String[] strings,
                                                                        Bundle bundle) throws NetworkErrorException {
                                                                    return null;
                                                                }
                                                                // Ignore attempts to confirm credentials
                                                                @Override
                                                                public Bundle confirmCredentials(
                                                                        AccountAuthenticatorResponse r,
                                                                        Account account,
                                                                        Bundle bundle) throws NetworkErrorException {
                                                                    return null;
                                                                }
                                                                // Getting an authentication token is not supported
                                                                @Override
                                                                public Bundle getAuthToken(
                                                                        AccountAuthenticatorResponse r,
                                                                        Account account,
                                                                        String s,
                                                                        Bundle bundle) throws NetworkErrorException {
                                                                    throw new UnsupportedOperationException();
                                                                }
                                                                // Getting a label for the auth token is not supported
                                                                @Override
                                                                public String getAuthTokenLabel(String s) {
                                                                    throw new UnsupportedOperationException();
                                                                }
                                                                // Updating user credentials is not supported
                                                                @Override
                                                                public Bundle updateCredentials(
                                                                        AccountAuthenticatorResponse r,
                                                                        Account account,
                                                                        String s, Bundle bundle) throws NetworkErrorException {
                                                                    throw new UnsupportedOperationException();
                                                                }
                                                                // Checking features for the account is not supported
                                                                @Override
                                                                public Bundle hasFeatures(
                                                                    AccountAuthenticatorResponse r,
                                                                    Account account, String[] strings) throws NetworkErrorException {
                                                                    throw new UnsupportedOperationException();
                                                                }
                                                            }
                                                            

                                                            Bind the Authenticator to the Framework

                                                            In order for the sync adapter framework to access your authenticator, you must create a bound Service for it. This service provides an Android binder object that allows the framework to call your authenticator and pass data between the authenticator and the framework.

                                                            Since the framework starts this Service the first time it needs to access the authenticator, you can also use the service to instantiate the authenticator, by calling the authenticator constructor in the Service.onCreate() method of the service.

                                                            The following snippet shows you how to define the bound Service:

                                                            /**
                                                             * A bound Service that instantiates the authenticator
                                                             * when started.
                                                             */
                                                            public class AuthenticatorService extends Service {
                                                                ...
                                                                // Instance field that stores the authenticator object
                                                                private Authenticator mAuthenticator;
                                                                @Override
                                                                public void onCreate() {
                                                                    // Create a new authenticator object
                                                                    mAuthenticator = new Authenticator(this);
                                                                }
                                                                /*
                                                                 * When the system binds to this Service to make the RPC call
                                                                 * return the authenticator's IBinder.
                                                                 */
                                                                @Override
                                                                public IBinder onBind(Intent intent) {
                                                                    return mAuthenticator.getIBinder();
                                                                }
                                                            }
                                                            

                                                            Add the Authenticator Metadata File

                                                            To plug your authenticator component into the sync adapter and account frameworks, you need to provide these framework with metadata that describes the component. This metadata declares the account type you've created for your sync adapter and declares user interface elements that the system displays if you want to make your account type visible to the user. Declare this metadata in a XML file stored in the /res/xml/ directory in your app project. You can give any name to the file, although it's usually called authenticator.xml.

                                                            This XML file contains a single element <account-authenticator> that has the following attributes:

                                                            android:accountType
                                                            The sync adapter framework requires each sync adapter to have an account type, in the form of a domain name. The framework uses the account type as part of the sync adapter's internal identification. For servers that require login, the account type along with a user account is sent to the server as part of the login credentials.

                                                            If your server doesn't require login, you still have to provide an account type. For the value, use a domain name that you control. While the framework uses it to manage your sync adapter, the value is not sent to your server.

                                                            android:icon
                                                            Pointer to a Drawable resource containing an icon. If you make the sync adapter visible by specifying the attribute android:userVisible="true" in res/xml/syncadapter.xml, then you must provide this icon resource. It appears in the Accounts section of the system's Settings app.
                                                            android:smallIcon
                                                            Pointer to a Drawable resource containing a small version of the icon. This resource may be used instead of android:icon in the Accounts section of the system's Settings app, depending on the screen size.
                                                            android:label
                                                            Localizable string that identifies the account type to users. If you make the sync adapter visible by specifying the attribute android:userVisible="true" in res/xml/syncadapter.xml, then you should provide this string. It appears in the Accounts section of the system's Settings app, next to the icon you define for the authenticator.

                                                            The following snippet shows the XML file for the authenticator you created previously:

                                                            <?xml version="1.0" encoding="utf-8"?>
                                                            <account-authenticator
                                                                    xmlns:android="http://schemas.android.com/apk/res/android"
                                                                    android:accountType="example.com"
                                                                    android:icon="@drawable/ic_launcher"
                                                                    android:smallIcon="@drawable/ic_launcher"
                                                                    android:label="@string/app_name"/>
                                                            

                                                            Declare the Authenticator in the Manifest

                                                            In a previous step, you created a bound Service that links the authenticator to the sync adapter framework. To identify this service to the system, declare it in your app manifest by adding the following <service> element as a child element of <application>:

                                                                <service
                                                                        android:name="com.example.android.syncadapter.AuthenticatorService">
                                                                    <intent-filter>
                                                                        <action android:name="android.accounts.AccountAuthenticator"/>
                                                                    </intent-filter>
                                                                    <meta-data
                                                                        android:name="android.accounts.AccountAuthenticator"
                                                                        android:resource="@xml/authenticator" />
                                                                </service>
                                                            

                                                            The <intent-filter> element sets up a filter that's triggered by the intent action android.accounts.AccountAuthenticator, which sent by the system to run the authenticator. When the filter is triggered, the system starts AuthenticatorService, the bound Service you have provided to wrap the authenticator.

                                                            The <meta-data> element declares the metadata for the authenticator. The android:name attribute links the meta-data to the authentication framework. The android:resource element specifies the name of the authenticator metadata file you created previously.

                                                            Besides an authenticator, a sync adapter also requires a content provider. If your app doesn't use a content provider already, go to the next lesson to learn how to create a stub content provider; otherwise, go to the lesson Creating a Sync Adapter.

                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                            This class requires API level or higher

                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                            Creating a Stub Content Provider | Android Developers Skip to content

                                                            Most visited

                                                            Recently visited

                                                            navigation

                                                              Creating a Stub Content Provider

                                                              This lesson teaches you to

                                                              1. Add a Stub Content Provider
                                                              2. Declare the Provider in the Manifest

                                                              You should also read

                                                              Try it out

                                                              Download the sample

                                                              BasicSyncAdapter.zip

                                                              The sync adapter framework is designed to work with device data managed by the flexible and highly secure content provider framework. For this reason, the sync adapter framework expects that an app that uses the framework has already defined a content provider for its local data. If the sync adapter framework tries to run your sync adapter, and your app doesn't have a content provider, your sync adapter crashes.

                                                              If you're developing a new app that transfers data from a server to the device, you should strongly consider storing the local data in a content provider. Besides their importance for sync adapters, content providers offer a variety of security benefits and are specifically designed to handle data storage on Android systems. To learn more about creating a content provider, see Creating a Content Provider.

                                                              However, if you're already storing local data in another form, you can still use a sync adapter to handle data transfer. To satisfy the sync adapter framework requirement for a content provider, add a stub content provider to your app. A stub provider implements the content provider class, but all of its required methods return null or 0. If you add a stub provider, you can then use a sync adapter to transfer data from any storage mechanism you choose.

                                                              If you already have a content provider in your app, you don't need a stub content provider. In that case, you can skip this lesson and proceed to the lesson Creating a Sync Adapter. If you don't yet have a content provider, this lesson shows you how to add a stub content provider that allows you to plug your sync adapter into the framework.

                                                              Add a Stub Content Provider

                                                              To create a stub content provider for your app, extend the class ContentProvider and stub out its required methods. The following snippet shows you how to create the stub provider:

                                                              /*
                                                               * Define an implementation of ContentProvider that stubs out
                                                               * all methods
                                                               */
                                                              public class StubProvider extends ContentProvider {
                                                                  /*
                                                                   * Always return true, indicating that the
                                                                   * provider loaded correctly.
                                                                   */
                                                                  @Override
                                                                  public boolean onCreate() {
                                                                      return true;
                                                                  }
                                                                  /*
                                                                   * Return no type for MIME type
                                                                   */
                                                                  @Override
                                                                  public String getType(Uri uri) {
                                                                      return null;
                                                                  }
                                                                  /*
                                                                   * query() always returns no results
                                                                   *
                                                                   */
                                                                  @Override
                                                                  public Cursor query(
                                                                          Uri uri,
                                                                          String[] projection,
                                                                          String selection,
                                                                          String[] selectionArgs,
                                                                          String sortOrder) {
                                                                      return null;
                                                                  }
                                                                  /*
                                                                   * insert() always returns null (no URI)
                                                                   */
                                                                  @Override
                                                                  public Uri insert(Uri uri, ContentValues values) {
                                                                      return null;
                                                                  }
                                                                  /*
                                                                   * delete() always returns "no rows affected" (0)
                                                                   */
                                                                  @Override
                                                                  public int delete(Uri uri, String selection, String[] selectionArgs) {
                                                                      return 0;
                                                                  }
                                                                  /*
                                                                   * update() always returns "no rows affected" (0)
                                                                   */
                                                                  public int update(
                                                                          Uri uri,
                                                                          ContentValues values,
                                                                          String selection,
                                                                          String[] selectionArgs) {
                                                                      return 0;
                                                                  }
                                                              }
                                                              

                                                              Declare the Provider in the Manifest

                                                              The sync adapter framework verifies that your app has a content provider by checking that your app has declared a provider in its app manifest. To declare the stub provider in the manifest, add a <provider> element with the following attributes:

                                                              android:name="com.example.android.datasync.provider.StubProvider"
                                                              Specifies the fully-qualified name of the class that implements the stub content provider.
                                                              android:authorities="com.example.android.datasync.provider"
                                                              A URI authority that identifies the stub content provider. Make this value your app's package name with the string ".provider" appended to it. Even though you're declaring your stub provider to the system, nothing tries to access the provider itself.
                                                              android:exported="false"
                                                              Determines whether other apps can access the content provider. For your stub content provider, set the value to false, since there's no need to allow other apps to see the provider. This value doesn't affect the interaction between the sync adapter framework and the content provider.
                                                              android:syncable="true"
                                                              Sets a flag that indicates that the provider is syncable. If you set this flag to true, you don't have to call setIsSyncable() in your code. The flag allows the sync adapter framework to make data transfers with the content provider, but transfers only occur if you do them explicitly.

                                                              The following snippet shows you how to add the <provider> element to the app manifest:

                                                              <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                  package="com.example.android.network.sync.BasicSyncAdapter"
                                                                  android:versionCode="1"
                                                                  android:versionName="1.0" >
                                                                  <application
                                                                      android:allowBackup="true"
                                                                      android:icon="@drawable/ic_launcher"
                                                                      android:label="@string/app_name"
                                                                      android:theme="@style/AppTheme" >
                                                                  ...
                                                                  <provider
                                                                      android:name="com.example.android.datasync.provider.StubProvider"
                                                                      android:authorities="com.example.android.datasync.provider"
                                                                      android:exported="false"
                                                                      android:syncable="true"/>
                                                                  ...
                                                                  </application>
                                                              </manifest>
                                                              

                                                              Now that you have created the dependencies required by the sync adapter framework, you can create the component that encapsulates your data transfer code. This component is called a sync adapter. The next lesson shows you how to add this component to your app.

                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                              This class requires API level or higher

                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                              Creating a Sync Adapter | Android Developers Skip to content

                                                              Most visited

                                                              Recently visited

                                                              navigation

                                                                Creating a Sync Adapter

                                                                The sync adapter component in your app encapsulates the code for the tasks that transfer data between the device and a server. Based on the scheduling and triggers you provide in your app, the sync adapter framework runs the code in the sync adapter component. To add a sync adapter component to your app, you need to add the following pieces:

                                                                Sync adapter class.
                                                                A class that wraps your data transfer code in an interface compatible with the sync adapter framework.
                                                                Bound Service.
                                                                A component that allows the sync adapter framework to run the code in your sync adapter class.
                                                                Sync adapter XML metadata file.
                                                                A file containing information about your sync adapter. The framework reads this file to find out how to load and schedule your data transfer.
                                                                Declarations in the app manifest.
                                                                XML that declares the bound service and points to sync adapter-specific metadata.

                                                                This lesson shows you how to define these elements.

                                                                Create a Sync Adapter Class

                                                                In this part of the lesson you learn how to create the sync adapter class that encapsulates the data transfer code. Creating the class includes extending the sync adapter base class, defining constructors for the class, and implementing the method where you define the data transfer tasks.

                                                                Extend the base sync adapter class AbstractThreadedSyncAdapter

                                                                To create the sync adapter component, start by extending AbstractThreadedSyncAdapter and writing its constructors. Use the constructors to run setup tasks each time your sync adapter component is created from scratch, just as you use Activity.onCreate() to set up an activity. For example, if your app uses a content provider to store data, use the constructors to get a ContentResolver instance. Since a second form of the constructor was added in Android platform version 3.0 to support the parallelSyncs argument, you need to create two forms of the constructor to maintain compatibility.

                                                                Note: The sync adapter framework is designed to work with sync adapter components that are singleton instances. Instantiating the sync adapter component is covered in more detail in the section Bind the Sync Adapter to the Framework.

                                                                The following example shows you how to implement AbstractThreadedSyncAdapterand its constructors:

                                                                /**
                                                                 * Handle the transfer of data between a server and an
                                                                 * app, using the Android sync adapter framework.
                                                                 */
                                                                public class SyncAdapter extends AbstractThreadedSyncAdapter {
                                                                    ...
                                                                    // Global variables
                                                                    // Define a variable to contain a content resolver instance
                                                                    ContentResolver mContentResolver;
                                                                    /**
                                                                     * Set up the sync adapter
                                                                     */
                                                                    public SyncAdapter(Context context, boolean autoInitialize) {
                                                                        super(context, autoInitialize);
                                                                        /*
                                                                         * If your app uses a content resolver, get an instance of it
                                                                         * from the incoming Context
                                                                         */
                                                                        mContentResolver = context.getContentResolver();
                                                                    }
                                                                    ...
                                                                    /**
                                                                     * Set up the sync adapter. This form of the
                                                                     * constructor maintains compatibility with Android 3.0
                                                                     * and later platform versions
                                                                     */
                                                                    public SyncAdapter(
                                                                            Context context,
                                                                            boolean autoInitialize,
                                                                            boolean allowParallelSyncs) {
                                                                        super(context, autoInitialize, allowParallelSyncs);
                                                                        /*
                                                                         * If your app uses a content resolver, get an instance of it
                                                                         * from the incoming Context
                                                                         */
                                                                        mContentResolver = context.getContentResolver();
                                                                        ...
                                                                    }
                                                                

                                                                Add the data transfer code to onPerformSync()

                                                                The sync adapter component does not automatically do data transfer. Instead, it encapsulates your data transfer code, so that the sync adapter framework can run the data transfer in the background, without involvement from your app. When the framework is ready to sync your application's data, it invokes your implementation of the method onPerformSync().

                                                                To facilitate the transfer of data from your main app code to the sync adapter component, the sync adapter framework calls onPerformSync() with the following arguments:

                                                                Account
                                                                An Account object associated with the event that triggered the sync adapter. If your server doesn't use accounts, you don't need to use the information in this object.
                                                                Extras
                                                                A Bundle containing flags sent by the event that triggered the sync adapter.
                                                                Authority
                                                                The authority of a content provider in the system. Your app has to have access to this provider. Usually, the authority corresponds to a content provider in your own app.
                                                                Content provider client
                                                                A ContentProviderClient for the content provider pointed to by the authority argument. A ContentProviderClient is a lightweight public interface to a content provider. It has the same basic functionality as a ContentResolver. If you're using a content provider to store data for your app, you can connect to the provider with this object. Otherwise, you can ignore it.
                                                                Sync result
                                                                A SyncResult object that you use to send information to the sync adapter framework.

                                                                The following snippet shows the overall structure of onPerformSync():

                                                                    /*
                                                                     * Specify the code you want to run in the sync adapter. The entire
                                                                     * sync adapter runs in a background thread, so you don't have to set
                                                                     * up your own background processing.
                                                                     */
                                                                    @Override
                                                                    public void onPerformSync(
                                                                            Account account,
                                                                            Bundle extras,
                                                                            String authority,
                                                                            ContentProviderClient provider,
                                                                            SyncResult syncResult) {
                                                                    /*
                                                                     * Put the data transfer code here.
                                                                     */
                                                                    ...
                                                                    }
                                                                

                                                                While the actual implementation of onPerformSync() is specific to your app's data synchronization requirements and server connection protocols, there are a few general tasks your implementation should perform:

                                                                Connecting to a server
                                                                Although you can assume that the network is available when your data transfer starts, the sync adapter framework doesn't automatically connect to a server.
                                                                Downloading and uploading data
                                                                A sync adapter doesn't automate any data transfer tasks. If you want to download data from a server and store it in a content provider, you have to provide the code that requests the data, downloads it, and inserts it in the provider. Similarly, if you want to send data to a server, you have to read it from a file, database, or provider, and send the necessary upload request. You also have to handle network errors that occur while your data transfer is running.
                                                                Handling data conflicts or determining how current the data is
                                                                A sync adapter doesn't automatically handle conflicts between data on the server and data on the device. Also, it doesn't automatically detect if the data on the server is newer than the data on the device, or vice versa. Instead, you have to provide your own algorithms for handling this situation.
                                                                Clean up.
                                                                Always close connections to a server and clean up temp files and caches at the end of your data transfer.

                                                                Note: The sync adapter framework runs onPerformSync() on a background thread, so you don't have to set up your own background processing.

                                                                In addition to your sync-related tasks, you should try to combine your regular network-related tasks and add them to onPerformSync(). By concentrating all of your network tasks in this method, you conserve the battery power that's needed to start and stop the network interfaces. To learn more about making network access more efficient, see the training class Transferring Data Without Draining the Battery, which describes several network access tasks you can include in your data transfer code.

                                                                Bind the Sync Adapter to the Framework

                                                                You now have your data transfer code encapsulated in a sync adapter component, but you have to provide the framework with access to your code. To do this, you need to create a bound Service that passes a special Android binder object from the sync adapter component to the framework. With this binder object, the framework can invoke the onPerformSync() method and pass data to it.

                                                                Instantiate your sync adapter component as a singleton in the onCreate() method of the service. By instantiating the component in onCreate(), you defer creating it until the service starts, which happens when the framework first tries to run your data transfer. You need to instantiate the component in a thread-safe manner, in case the sync adapter framework queues up multiple executions of your sync adapter in response to triggers or scheduling.

                                                                For example, the following snippet shows you how to create a class that implements the bound Service, instantiates your sync adapter component, and gets the Android binder object:

                                                                package com.example.android.syncadapter;
                                                                /**
                                                                 * Define a Service that returns an IBinder for the
                                                                 * sync adapter class, allowing the sync adapter framework to call
                                                                 * onPerformSync().
                                                                 */
                                                                public class SyncService extends Service {
                                                                    // Storage for an instance of the sync adapter
                                                                    private static SyncAdapter sSyncAdapter = null;
                                                                    // Object to use as a thread-safe lock
                                                                    private static final Object sSyncAdapterLock = new Object();
                                                                    /*
                                                                     * Instantiate the sync adapter object.
                                                                     */
                                                                    @Override
                                                                    public void onCreate() {
                                                                        /*
                                                                         * Create the sync adapter as a singleton.
                                                                         * Set the sync adapter as syncable
                                                                         * Disallow parallel syncs
                                                                         */
                                                                        synchronized (sSyncAdapterLock) {
                                                                            if (sSyncAdapter == null) {
                                                                                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
                                                                            }
                                                                        }
                                                                    }
                                                                    /**
                                                                     * Return an object that allows the system to invoke
                                                                     * the sync adapter.
                                                                     *
                                                                     */
                                                                    @Override
                                                                    public IBinder onBind(Intent intent) {
                                                                        /*
                                                                         * Get the object that allows external processes
                                                                         * to call onPerformSync(). The object is created
                                                                         * in the base class code when the SyncAdapter
                                                                         * constructors call super()
                                                                         */
                                                                        return sSyncAdapter.getSyncAdapterBinder();
                                                                    }
                                                                }
                                                                

                                                                Note: To see a more detailed example of a bound service for a sync adapter, see the sample app.

                                                                Add the Account Required by the Framework

                                                                The sync adapter framework requires each sync adapter to have an account type. You declared the account type value in the section Add the Authenticator Metadata File. Now you have to set up this account type in the Android system. To set up the account type, add a dummy account that uses the account type by calling addAccountExplicitly().

                                                                The best place to call the method is in the onCreate() method of your app's opening activity. The following code snippet shows you how to do this:

                                                                public class MainActivity extends FragmentActivity {
                                                                    ...
                                                                    ...
                                                                    // Constants
                                                                    // The authority for the sync adapter's content provider
                                                                    public static final String AUTHORITY = "com.example.android.datasync.provider";
                                                                    // An account type, in the form of a domain name
                                                                    public static final String ACCOUNT_TYPE = "example.com";
                                                                    // The account name
                                                                    public static final String ACCOUNT = "dummyaccount";
                                                                    // Instance fields
                                                                    Account mAccount;
                                                                    ...
                                                                    @Override
                                                                    protected void onCreate(Bundle savedInstanceState) {
                                                                        super.onCreate(savedInstanceState);
                                                                        ...
                                                                        // Create the dummy account
                                                                        mAccount = CreateSyncAccount(this);
                                                                        ...
                                                                    }
                                                                    ...
                                                                    /**
                                                                     * Create a new dummy account for the sync adapter
                                                                     *
                                                                     * @param context The application context
                                                                     */
                                                                    public static Account CreateSyncAccount(Context context) {
                                                                        // Create the account type and default account
                                                                        Account newAccount = new Account(
                                                                                ACCOUNT, ACCOUNT_TYPE);
                                                                        // Get an instance of the Android account manager
                                                                        AccountManager accountManager =
                                                                                (AccountManager) context.getSystemService(
                                                                                        ACCOUNT_SERVICE);
                                                                        /*
                                                                         * Add the account and account type, no password or user data
                                                                         * If successful, return the Account object, otherwise report an error.
                                                                         */
                                                                        if (accountManager.addAccountExplicitly(newAccount, null, null)) {
                                                                            /*
                                                                             * If you don't set android:syncable="true" in
                                                                             * in your <provider> element in the manifest,
                                                                             * then call context.setIsSyncable(account, AUTHORITY, 1)
                                                                             * here.
                                                                             */
                                                                        } else {
                                                                            /*
                                                                             * The account exists or some other error occurred. Log this, report it,
                                                                             * or handle it internally.
                                                                             */
                                                                        }
                                                                    }
                                                                    ...
                                                                }
                                                                

                                                                Add the Sync Adapter Metadata File

                                                                To plug your sync adapter component into the framework, you need to provide the framework with metadata that describes the component and provides additional flags. The metadata specifies the account type you've created for your sync adapter, declares a content provider authority associated with your app, controls a part of the system user interface related to sync adapters, and declares other sync-related flags. Declare this metadata in a special XML file stored in the /res/xml/ directory in your app project. You can give any name to the file, although it's usually called syncadapter.xml.

                                                                This XML file contains a single XML element <sync-adapter> that has the following attributes:

                                                                android:contentAuthority
                                                                The URI authority for your content provider. If you created a stub content provider for your app in the previous lesson Creating a Stub Content Provider, use the value you specified for the attribute android:authorities in the <provider> element you added to your app manifest. This attribute is described in more detail in the section Declare the Provider in the Manifest.
                                                                If you're transferring data from a content provider to a server with your sync adapter, this value should be the same as the content URI authority you're using for that data. This value is also one of the authorities you specify in the android:authorities attribute of the <provider> element that declares your provider in your app manifest.
                                                                android:accountType
                                                                The account type required by the sync adapter framework. The value must be the same as the account type value you provided when you created the authenticator metadata file, as described in the section Add the Authenticator Metadata File. It's also the value you specified for the constant ACCOUNT_TYPE in the code snippet in the section Add the Account Required by the Framework.
                                                                Settings attributes
                                                                android:userVisible
                                                                Sets the visibility of the sync adapter's account type. By default, the account icon and label associated with the account type are visible in the Accounts section of the system's Settings app, so you should make your sync adapter invisible unless you have an account type or domain that's easily associated with your app. If you make your account type invisible, you can still allow users to control your sync adapter with a user interface in one of your app's activities.
                                                                android:supportsUploading
                                                                Allows you to upload data to the cloud. Set this to false if your app only downloads data.
                                                                android:allowParallelSyncs
                                                                Allows multiple instances of your sync adapter component to run at the same time. Use this if your app supports multiple user accounts and you want to allow multiple users to transfer data in parallel. This flag has no effect if you never run multiple data transfers.
                                                                android:isAlwaysSyncable
                                                                Indicates to the sync adapter framework that it can run your sync adapter at any time you've specified. If you want to programmatically control when your sync adapter can run, set this flag to false, and then call requestSync() to run the sync adapter. To learn more about running a sync adapter, see the lesson Running a Sync Adapter

                                                                The following example shows the XML for a sync adapter that uses a single dummy account and only does downloads.

                                                                <?xml version="1.0" encoding="utf-8"?>
                                                                <sync-adapter
                                                                        xmlns:android="http://schemas.android.com/apk/res/android"
                                                                        android:contentAuthority="com.example.android.datasync.provider"
                                                                        android:accountType="com.android.example.datasync"
                                                                        android:userVisible="false"
                                                                        android:supportsUploading="false"
                                                                        android:allowParallelSyncs="false"
                                                                        android:isAlwaysSyncable="true"/>
                                                                

                                                                Declare the Sync Adapter in the Manifest

                                                                Once you've added the sync adapter component to your app, you have to request permissions related to using the component, and you have to declare the bound Service you've added.

                                                                Since the sync adapter component runs code that transfers data between the network and the device, you need to request permission to access the Internet. In addition, your app needs to request permission to read and write sync adapter settings, so you can control the sync adapter programmatically from other components in your app. You also need to request a special permission that allows your app to use the authenticator component you created in the lesson Creating a Stub Authenticator.

                                                                To request these permissions, add the following to your app manifest as child elements of <manifest>:

                                                                android.permission.INTERNET
                                                                Allows the sync adapter code to access the Internet so that it can download or upload data from the device to a server. You don't need to add this permission again if you were requesting it previously.
                                                                android.permission.READ_SYNC_SETTINGS
                                                                Allows your app to read the current sync adapter settings. For example, you need this permission in order to call getIsSyncable().
                                                                android.permission.WRITE_SYNC_SETTINGS
                                                                Allows your app to control sync adapter settings. You need this permission in order to set periodic sync adapter runs using addPeriodicSync(). This permission is not required to call requestSync(). To learn more about running the sync adapter, see Running A Sync Adapter.

                                                                The following snippet shows how to add the permissions:

                                                                <manifest>
                                                                ...
                                                                    <uses-permission
                                                                            android:name="android.permission.INTERNET"/>
                                                                    <uses-permission
                                                                            android:name="android.permission.READ_SYNC_SETTINGS"/>
                                                                    <uses-permission
                                                                            android:name="android.permission.WRITE_SYNC_SETTINGS"/>
                                                                    <uses-permission
                                                                            android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
                                                                ...
                                                                </manifest>
                                                                

                                                                Finally, to declare the bound Service that the framework uses to interact with your sync adapter, add the following XML to your app manifest as a child element of <application>:

                                                                        <service
                                                                                android:name="com.example.android.datasync.SyncService"
                                                                                android:exported="true"
                                                                                android:process=":sync">
                                                                            <intent-filter>
                                                                                <action android:name="android.content.SyncAdapter"/>
                                                                            </intent-filter>
                                                                            <meta-data android:name="android.content.SyncAdapter"
                                                                                    android:resource="@xml/syncadapter" />
                                                                        </service>
                                                                

                                                                The <intent-filter> element sets up a filter that's triggered by the intent action android.content.SyncAdapter, sent by the system to run the sync adapter. When the filter is triggered, the system starts the bound service you've created, which in this example is SyncService. The attribute android:exported="true" allows processes other than your app (including the system) to access the Service. The attribute android:process=":sync" tells the system to run the Service in a global shared process named sync. If you have multiple sync adapters in your app they can share this process, which reduces overhead.

                                                                The <meta-data> element provides provides the name of the sync adapter metadata XML file you created previously. The android:name attribute indicates that this metadata is for the sync adapter framework. The android:resource element specifies the name of the metadata file.

                                                                You now have all of the components for your sync adapter. The next lesson shows you how to tell the sync adapter framework to run your sync adapter, either in response to an event or on a regular schedule.

                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                This class requires API level or higher

                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                Running a Sync Adapter | Android Developers Skip to content

                                                                Most visited

                                                                Recently visited

                                                                navigation

                                                                  Running a Sync Adapter

                                                                  In the previous lessons in this class, you learned how to create a sync adapter component that encapsulates data transfer code, and how to add the additional components that allow you to plug the sync adapter into the system. You now have everything you need to install an app that includes a sync adapter, but none of the code you've seen actually runs the sync adapter.

                                                                  You should try to run your sync adapter based on a schedule or as the indirect result of some event. For example, you may want your sync adapter to run on a regular schedule, either after a certain period of time or at a particular time of the day. You may also want to run your sync adapter when there are changes to data stored on the device. You should avoid running your sync adapter as the direct result of a user action, because by doing this you don't get the full benefit of the sync adapter framework's scheduling ability. For example, you should avoid providing a refresh button in your user interface.

                                                                  You have the following options for running your sync adapter:

                                                                  When server data changes
                                                                  Run the sync adapter in response to a message from a server, indicating that server-based data has changed. This option allows you to refresh data from the server to the device without degrading performance or wasting battery life by polling the server.
                                                                  When device data changes
                                                                  Run a sync adapter when data changes on the device. This option allows you to send modified data from the device to a server, and is especially useful if you need to ensure that the server always has the latest device data. This option is straightforward to implement if you actually store data in your content provider. If you're using a stub content provider, detecting data changes may be more difficult.
                                                                  At regular intervals
                                                                  Run a sync adapter after the expiration of an interval you choose, or run it at a certain time every day.
                                                                  On demand
                                                                  Run the sync adapter in response to a user action. However, to provide the best user experience you should rely primarily on one of the more automated options. By using automated options, you conserve battery and network resources.

                                                                  The rest of this lesson describes each of the options in more detail.

                                                                  Run the Sync Adapter When Server Data Changes

                                                                  If your app transfers data from a server and the server data changes frequently, you can use a sync adapter to do downloads in response to data changes. To run the sync adapter, have the server send a special message to a BroadcastReceiver in your app. In response to this message, call ContentResolver.requestSync() to signal the sync adapter framework to run your sync adapter.

                                                                  Google Cloud Messaging (GCM) provides both the server and device components you need to make this messaging system work. Using GCM to trigger transfers is more reliable and more efficient than polling servers for status. While polling requires a Service that is always active, GCM uses a BroadcastReceiver that's activated when a message arrives. While polling at regular intervals uses battery power even if no updates are available, GCM only sends messages when needed.

                                                                  Note: If you use GCM to trigger your sync adapter via a broadcast to all devices where your app is installed, remember that they receive your message at roughly the same time. This situation can cause multiple instance of your sync adapter to run at the same time, causing server and network overload. To avoid this situation for a broadcast to all devices, you should consider deferring the start of the sync adapter for a period that's unique for each device.

                                                                  The following code snippet shows you how to run requestSync() in response to an incoming GCM message:

                                                                  public class GcmBroadcastReceiver extends BroadcastReceiver {
                                                                      ...
                                                                      // Constants
                                                                      // Content provider authority
                                                                      public static final String AUTHORITY = "com.example.android.datasync.provider"
                                                                      // Account type
                                                                      public static final String ACCOUNT_TYPE = "com.example.android.datasync";
                                                                      // Account
                                                                      public static final String ACCOUNT = "default_account";
                                                                      // Incoming Intent key for extended data
                                                                      public static final String KEY_SYNC_REQUEST =
                                                                              "com.example.android.datasync.KEY_SYNC_REQUEST";
                                                                      ...
                                                                      @Override
                                                                      public void onReceive(Context context, Intent intent) {
                                                                          // Get a GCM object instance
                                                                          GoogleCloudMessaging gcm =
                                                                                  GoogleCloudMessaging.getInstance(context);
                                                                          // Get the type of GCM message
                                                                          String messageType = gcm.getMessageType(intent);
                                                                          /*
                                                                           * Test the message type and examine the message contents.
                                                                           * Since GCM is a general-purpose messaging system, you
                                                                           * may receive normal messages that don't require a sync
                                                                           * adapter run.
                                                                           * The following code tests for a a boolean flag indicating
                                                                           * that the message is requesting a transfer from the device.
                                                                           */
                                                                          if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)
                                                                              &&
                                                                              intent.getBooleanExtra(KEY_SYNC_REQUEST)) {
                                                                              /*
                                                                               * Signal the framework to run your sync adapter. Assume that
                                                                               * app initialization has already created the account.
                                                                               */
                                                                              ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
                                                                              ...
                                                                          }
                                                                          ...
                                                                      }
                                                                      ...
                                                                  }
                                                                  

                                                                  Run the Sync Adapter When Content Provider Data Changes

                                                                  If your app collects data in a content provider, and you want to update the server whenever you update the provider, you can set up your app to run your sync adapter automatically. To do this, you register an observer for the content provider. When data in your content provider changes, the content provider framework calls the observer. In the observer, call requestSync() to tell the framework to run your sync adapter.

                                                                  Note: If you're using a stub content provider, you don't have any data in the content provider and onChange() is never called. In this case, you have to provide your own mechanism for detecting changes to device data. This mechanism is also responsible for calling requestSync() when the data changes.

                                                                  To create an observer for your content provider, extend the class ContentObserver and implement both forms of its onChange() method. In onChange(), call requestSync() to start the sync adapter.

                                                                  To register the observer, pass it as an argument in a call to registerContentObserver(). In this call, you also have to pass in a content URI for the data you want to watch. The content provider framework compares this watch URI to content URIs passed in as arguments to ContentResolver methods that modify your provider, such as ContentResolver.insert(). If there's a match, your implementation of ContentObserver.onChange() is called.

                                                                  The following code snippet shows you how to define a ContentObserver that calls requestSync() when a table changes:

                                                                  public class MainActivity extends FragmentActivity {
                                                                      ...
                                                                      // Constants
                                                                      // Content provider scheme
                                                                      public static final String SCHEME = "content://";
                                                                      // Content provider authority
                                                                      public static final String AUTHORITY = "com.example.android.datasync.provider";
                                                                      // Path for the content provider table
                                                                      public static final String TABLE_PATH = "data_table";
                                                                      // Account
                                                                      public static final String ACCOUNT = "default_account";
                                                                      // Global variables
                                                                      // A content URI for the content provider's data table
                                                                      Uri mUri;
                                                                      // A content resolver for accessing the provider
                                                                      ContentResolver mResolver;
                                                                      ...
                                                                      public class TableObserver extends ContentObserver {
                                                                          /*
                                                                           * Define a method that's called when data in the
                                                                           * observed content provider changes.
                                                                           * This method signature is provided for compatibility with
                                                                           * older platforms.
                                                                           */
                                                                          @Override
                                                                          public void onChange(boolean selfChange) {
                                                                              /*
                                                                               * Invoke the method signature available as of
                                                                               * Android platform version 4.1, with a null URI.
                                                                               */
                                                                              onChange(selfChange, null);
                                                                          }
                                                                          /*
                                                                           * Define a method that's called when data in the
                                                                           * observed content provider changes.
                                                                           */
                                                                          @Override
                                                                          public void onChange(boolean selfChange, Uri changeUri) {
                                                                              /*
                                                                               * Ask the framework to run your sync adapter.
                                                                               * To maintain backward compatibility, assume that
                                                                               * changeUri is null.
                                                                               */
                                                                              ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
                                                                          }
                                                                          ...
                                                                      }
                                                                      ...
                                                                      @Override
                                                                      protected void onCreate(Bundle savedInstanceState) {
                                                                          super.onCreate(savedInstanceState);
                                                                          ...
                                                                          // Get the content resolver object for your app
                                                                          mResolver = getContentResolver();
                                                                          // Construct a URI that points to the content provider data table
                                                                          mUri = new Uri.Builder()
                                                                                    .scheme(SCHEME)
                                                                                    .authority(AUTHORITY)
                                                                                    .path(TABLE_PATH)
                                                                                    .build();
                                                                          /*
                                                                           * Create a content observer object.
                                                                           * Its code does not mutate the provider, so set
                                                                           * selfChange to "false"
                                                                           */
                                                                          TableObserver observer = new TableObserver(false);
                                                                          /*
                                                                           * Register the observer for the data table. The table's path
                                                                           * and any of its subpaths trigger the observer.
                                                                           */
                                                                          mResolver.registerContentObserver(mUri, true, observer);
                                                                          ...
                                                                      }
                                                                      ...
                                                                  }
                                                                  

                                                                  Run the Sync Adapter Periodically

                                                                  You can run your sync adapter periodically by setting a period of time to wait between runs, or by running it at certain times of the day, or both. Running your sync adapter periodically allows you to roughly match the update interval of your server.

                                                                  Similarly, you can upload data from the device when your server is relatively idle, by scheduling your sync adapter to run at night. Most users leave their powered on and plugged in at night, so this time is usually available. Moreover, the device is not running other tasks at the same time as your sync adapter. If you take this approach, however, you need to ensure that each device triggers a data transfer at a slightly different time. If all devices run your sync adapter at the same time, you are likely to overload your server and cell provider data networks.

                                                                  In general, periodic runs make sense if your users don't need instant updates, but expect to have regular updates. Periodic runs also make sense if you want to balance the availability of up-to-date data with the efficiency of smaller sync adapter runs that don't over-use device resources.

                                                                  To run your sync adapter at regular intervals, call addPeriodicSync(). This schedules your sync adapter to run after a certain amount of time has elapsed. Since the sync adapter framework has to account for other sync adapter executions and tries to maximize battery efficiency, the elapsed time may vary by a few seconds. Also, the framework won't run your sync adapter if the network is not available.

                                                                  Notice that addPeriodicSync() doesn't run the sync adapter at a particular time of day. To run your sync adapter at roughly the same time every day, use a repeating alarm as a trigger. Repeating alarms are described in more detail in the reference documentation for AlarmManager. If you use the method setInexactRepeating() to set time-of-day triggers that have some variation, you should still randomize the start time to ensure that sync adapter runs from different devices are staggered.

                                                                  The method addPeriodicSync() doesn't disable setSyncAutomatically(), so you may get multiple sync runs in a relatively short period of time. Also, only a few sync adapter control flags are allowed in a call to addPeriodicSync(); the flags that are not allowed are described in the referenced documentation for addPeriodicSync().

                                                                  The following code snippet shows you how to schedule periodic sync adapter runs:

                                                                  public class MainActivity extends FragmentActivity {
                                                                      ...
                                                                      // Constants
                                                                      // Content provider authority
                                                                      public static final String AUTHORITY = "com.example.android.datasync.provider";
                                                                      // Account
                                                                      public static final String ACCOUNT = "default_account";
                                                                      // Sync interval constants
                                                                      public static final long SECONDS_PER_MINUTE = 60L;
                                                                      public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
                                                                      public static final long SYNC_INTERVAL =
                                                                              SYNC_INTERVAL_IN_MINUTES *
                                                                              SECONDS_PER_MINUTE;
                                                                      // Global variables
                                                                      // A content resolver for accessing the provider
                                                                      ContentResolver mResolver;
                                                                      ...
                                                                      @Override
                                                                      protected void onCreate(Bundle savedInstanceState) {
                                                                          super.onCreate(savedInstanceState);
                                                                          ...
                                                                          // Get the content resolver for your app
                                                                          mResolver = getContentResolver();
                                                                          /*
                                                                           * Turn on periodic syncing
                                                                           */
                                                                          ContentResolver.addPeriodicSync(
                                                                                  ACCOUNT,
                                                                                  AUTHORITY,
                                                                                  Bundle.EMPTY,
                                                                                  SYNC_INTERVAL);
                                                                          ...
                                                                      }
                                                                      ...
                                                                  }
                                                                  

                                                                  Run the Sync Adapter On Demand

                                                                  Running your sync adapter in response to a user request is the least preferable strategy for running a sync adapter. The framework is specifically designed to conserve battery power when it runs sync adapters according to a schedule. Options that run a sync in response to data changes use battery power effectively, since the power is used to provide new data.

                                                                  In comparison, allowing users to run a sync on demand means that the sync runs by itself, which is inefficient use of network and power resources. Also, providing sync on demand leads users to request a sync even if there's no evidence that the data has changed, and running a sync that doesn't refresh data is an ineffective use of battery power. In general, your app should either use other signals to trigger a sync or schedule them at regular intervals, without user input.

                                                                  However, if you still want to run the sync adapter on demand, set the sync adapter flags for a manual sync adapter run, then call ContentResolver.requestSync().

                                                                  Run on demand transfers with the following flags:

                                                                  SYNC_EXTRAS_MANUAL
                                                                  Forces a manual sync. The sync adapter framework ignores the existing settings, such as the flag set by setSyncAutomatically().
                                                                  SYNC_EXTRAS_EXPEDITED
                                                                  Forces the sync to start immediately. If you don't set this, the system may wait several seconds before running the sync request, because it tries to optimize battery use by scheduling many requests in a short period of time.

                                                                  The following code snippet shows you how to call requestSync() in response to a button click:

                                                                  public class MainActivity extends FragmentActivity {
                                                                      ...
                                                                      // Constants
                                                                      // Content provider authority
                                                                      public static final String AUTHORITY =
                                                                              "com.example.android.datasync.provider"
                                                                      // Account type
                                                                      public static final String ACCOUNT_TYPE = "com.example.android.datasync";
                                                                      // Account
                                                                      public static final String ACCOUNT = "default_account";
                                                                      // Instance fields
                                                                      Account mAccount;
                                                                      ...
                                                                      @Override
                                                                      protected void onCreate(Bundle savedInstanceState) {
                                                                          super.onCreate(savedInstanceState);
                                                                          ...
                                                                          /*
                                                                           * Create the dummy account. The code for CreateSyncAccount
                                                                           * is listed in the lesson Creating a Sync Adapter
                                                                           */
                                                                  
                                                                          mAccount = CreateSyncAccount(this);
                                                                          ...
                                                                      }
                                                                      /**
                                                                       * Respond to a button click by calling requestSync(). This is an
                                                                       * asynchronous operation.
                                                                       *
                                                                       * This method is attached to the refresh button in the layout
                                                                       * XML file
                                                                       *
                                                                       * @param v The View associated with the method call,
                                                                       * in this case a Button
                                                                       */
                                                                      public void onRefreshButtonClick(View v) {
                                                                          ...
                                                                          // Pass the settings flags by inserting them in a bundle
                                                                          Bundle settingsBundle = new Bundle();
                                                                          settingsBundle.putBoolean(
                                                                                  ContentResolver.SYNC_EXTRAS_MANUAL, true);
                                                                          settingsBundle.putBoolean(
                                                                                  ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
                                                                          /*
                                                                           * Request the sync for the default account, authority, and
                                                                           * manual sync settings
                                                                           */
                                                                          ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
                                                                      }
                                                                  
                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                  This class requires API level or higher

                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                  Transmitting Network Data Using Volley | Android Developers Skip to content

                                                                  Most visited

                                                                  Recently visited

                                                                  navigation

                                                                    Transmitting Network Data Using Volley

                                                                    Dependencies and prerequisites

                                                                    • Android 2.2 (API Level 8) or higher

                                                                    Video

                                                                    Volley: Easy, Fast Networking for Android

                                                                    Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster. Volley is available through the open AOSP repository.

                                                                    Volley offers the following benefits:

                                                                    • Automatic scheduling of network requests.
                                                                    • Multiple concurrent network connections.
                                                                    • Transparent disk and memory response caching with standard HTTP cache coherence.
                                                                    • Support for request prioritization.
                                                                    • Cancellation request API. You can cancel a single request, or you can set blocks or scopes of requests to cancel.
                                                                    • Ease of customization, for example, for retry and backoff.
                                                                    • Strong ordering that makes it easy to correctly populate your UI with data fetched asynchronously from the network.
                                                                    • Debugging and tracing tools.

                                                                    Volley excels at RPC-type operations used to populate a UI, such as fetching a page of search results as structured data. It integrates easily with any protocol and comes out of the box with support for raw strings, images, and JSON. By providing built-in support for the features you need, Volley frees you from writing boilerplate code and allows you to concentrate on the logic that is specific to your app.

                                                                    Volley is not suitable for large download or streaming operations, since Volley holds all responses in memory during parsing. For large download operations, consider using an alternative like DownloadManager.

                                                                    The core Volley library is developed in the open AOSP repository at frameworks/volley and contains the main request dispatch pipeline as well as a set of commonly applicable utilities, available in the Volley "toolbox." The easiest way to add Volley to your project is to clone the Volley repository and set it as a library project:

                                                                    1. Git clone the repository by typing the following at the command line:
                                                                      git clone https://android.googlesource.com/platform/frameworks/volley
                                                                      
                                                                    2. Import the downloaded source into your app project as an Android library module as described in Create an Android Library.

                                                                    Lessons

                                                                    Sending a Simple Request
                                                                    Learn how to send a simple request using the default behaviors of Volley, and how to cancel a request.
                                                                    Setting Up a RequestQueue
                                                                    Learn how to set up a RequestQueue, and how to implement a singleton pattern to create a RequestQueue that lasts the lifetime of your app.
                                                                    Making a Standard Request
                                                                    Learn how to send a request using one of Volley's out-of-the-box request types (raw strings, images, and JSON).
                                                                    Implementing a Custom Request
                                                                    Learn how to implement a custom request.
                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                    This class requires API level or higher

                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                    Building Apps with Location & Maps | Android Developers Skip to content

                                                                    Most visited

                                                                    Recently visited

                                                                    navigation

                                                                      Building Apps with Location & Maps

                                                                      These classes teach you how to add user location and mapping information to your app. Make your app more helpful and relevant to users by providing information about where they are and the world around them.

                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                      This class requires API level or higher

                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                      Making Your App Location-Aware | Android Developers Skip to content

                                                                      Most visited

                                                                      Recently visited

                                                                      navigation

                                                                        Making Your App Location-Aware

                                                                        Dependencies and prerequisites

                                                                        • Google Play services client library (latest version)
                                                                        • Android version 2.2 (API level 8) or later

                                                                        You should also read

                                                                        Video

                                                                        Activity Recognition

                                                                        One of the unique features of mobile applications is location awareness. Mobile users take their devices with them everywhere, and adding location awareness to your app offers users a more contextual experience. The location APIs available in Google Play services facilitate adding location awareness to your app with automated location tracking, geofencing, and activity recognition.

                                                                        The Google Play services location APIs are preferred over the Android framework location APIs (android.location) as a way of adding location awareness to your app. If you are currently using the Android framework location APIs, you are strongly encouraged to switch to the Google Play services location APIs as soon as possible.

                                                                        This class shows you how to use the Google Play services location APIs in your app to get the current location, get periodic location updates, and look up addresses. The class includes sample apps and code snippets that you can use as a starting point for adding location awareness to your app.

                                                                        Note: Since this class is based on the Google Play services client library, make sure you install the latest version before using the sample apps or code snippets. To learn how to set up the client library with the latest version, see Setup in the Google Play services guide.

                                                                        Lessons

                                                                        Getting the Last Known Location
                                                                        Learn how to retrieve the last known location of an Android device, which is usually equivalent to the user's current location.
                                                                        Changing Location Settings
                                                                        Learn how to detect and apply system settings for location features.
                                                                        Receiving Location Updates
                                                                        Learn how to request and receive periodic location updates.
                                                                        Displaying a Location Address
                                                                        Learn how to convert a location's latitude and longitude into an address (reverse geocoding).
                                                                        Creating and Monitoring Geofences
                                                                        Learn how to define one or more geographic areas as locations of interest, called geofences, and detect when the user is close to or inside a geofence.
                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                        This class requires API level or higher

                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                        Getting the Last Known Location | Android Developers Skip to content

                                                                        Most visited

                                                                        Recently visited

                                                                        navigation

                                                                          Getting the Last Known Location

                                                                          Using the Google Play services location APIs, your app can request the last known location of the user's device. In most cases, you are interested in the user's current location, which is usually equivalent to the last known location of the device.

                                                                          Specifically, use the fused location provider to retrieve the device's last known location. The fused location provider is one of the location APIs in Google Play services. It manages the underlying location technology and provides a simple API so that you can specify requirements at a high level, like high accuracy or low power. It also optimizes the device's use of battery power.

                                                                          This lesson shows you how to make a single request for the location of a device using the getLastLocation() method in the fused location provider.

                                                                          Set Up Google Play Services

                                                                          To access the fused location provider, your app's development project must include Google Play services. Download and install the Google Play services component via the SDK Manager and add the library to your project. For details, see the guide to Setting Up Google Play Services.

                                                                          Specify App Permissions

                                                                          Apps that use location services must request location permissions. Android offers two location permissions: ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION. The permission you choose determines the accuracy of the location returned by the API. If you specify ACCESS_COARSE_LOCATION, the API returns a location with an accuracy approximately equivalent to a city block.

                                                                          This lesson requires only coarse location. Request this permission with the uses-permission element in your app manifest, as the following code snippet shows:

                                                                          <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                              package="com.google.android.gms.location.sample.basiclocationsample" >
                                                                          
                                                                            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
                                                                          </manifest>
                                                                          

                                                                          Connect to Google Play Services

                                                                          To connect to the API, you need to create an instance of the Google Play services API client. For details about using the client, see the guide to Accessing Google APIs.

                                                                          In your activity's onCreate() method, create an instance of Google API Client, using the GoogleApiClient.Builder class to add the LocationServices API, as the following code snippet shows.

                                                                          // Create an instance of GoogleAPIClient.
                                                                          if (mGoogleApiClient == null) {
                                                                              mGoogleApiClient = new GoogleApiClient.Builder(this)
                                                                                  .addConnectionCallbacks(this)
                                                                                  .addOnConnectionFailedListener(this)
                                                                                  .addApi(LocationServices.API)
                                                                                  .build();
                                                                          }
                                                                          

                                                                          To connect, call connect() from the activity's onStart() method. To disconnect, call disconnect() from the activity's onStop() method. The following snippet shows an example of how to use both of these methods.

                                                                          protected void onStart() {
                                                                              mGoogleApiClient.connect();
                                                                              super.onStart();
                                                                          }
                                                                          
                                                                          protected void onStop() {
                                                                              mGoogleApiClient.disconnect();
                                                                              super.onStop();
                                                                          }
                                                                          

                                                                          Get the Last Known Location

                                                                          Once you have connected to Google Play services and the location services API, you can get the last known location of a user's device. When your app is connected to these you can use the fused location provider's getLastLocation() method to retrieve the device location. The precision of the location returned by this call is determined by the permission setting you put in your app manifest, as described in the Specify App Permissions section of this document.

                                                                          To request the last known location, call the getLastLocation() method, passing it your instance of the GoogleApiClient object. Do this in the onConnected() callback provided by Google API Client, which is called when the client is ready. The following code snippet illustrates the request and a simple handling of the response:

                                                                          public class MainActivity extends ActionBarActivity implements
                                                                                  ConnectionCallbacks, OnConnectionFailedListener {
                                                                              ...
                                                                              @Override
                                                                              public void onConnected(Bundle connectionHint) {
                                                                                  mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
                                                                                          mGoogleApiClient);
                                                                                  if (mLastLocation != null) {
                                                                                      mLatitudeText.setText(String.valueOf(mLastLocation.getLatitude()));
                                                                                      mLongitudeText.setText(String.valueOf(mLastLocation.getLongitude()));
                                                                                  }
                                                                              }
                                                                          }
                                                                          

                                                                          The getLastLocation() method returns a Location object from which you can retrieve the latitude and longitude coordinates of a geographic location. The location object returned may be null in rare cases when the location is not available.

                                                                          The next lesson, Changing Location Settings, shows you how to detect the current location settings, and prompt the user to change settings as appropriate for your app's requirements.

                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                          This class requires API level or higher

                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                          Changing Location Settings | Android Developers Skip to content

                                                                          Most visited

                                                                          Recently visited

                                                                          navigation

                                                                            Changing Location Settings

                                                                            If your app needs to request location or receive permission updates, the device needs to enable the appropriate system settings, such as GPS or Wi-Fi scanning. Rather than directly enabling services such as the device's GPS, your app specifies the required level of accuracy/power consumption and desired update interval, and the device automatically makes the appropriate changes to system settings. These settings are defined by the LocationRequest data object.

                                                                            This lesson shows you how to use the Settings API to check which settings are enabled, and present the Location Settings dialog for the user to update their settings with a single tap.

                                                                            Connect to Location Services

                                                                            In order to use the location services provided by Google Play Services and the fused location provider, connect your app using the Google API Client, then check the current location settings and prompt the user to enable the required settings if needed. For details on connecting with the Google API client, see Getting the Last Known Location.

                                                                            Apps that use location services must request location permissions. For this lesson, coarse location detection is sufficient. Request this permission with the uses-permission element in your app manifest, as shown in the following example:

                                                                            <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                              package="com.google.android.gms.location.sample.locationupdates" >
                                                                            
                                                                              <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
                                                                            </manifest>
                                                                            

                                                                            If the device is running Android 6.0 or higher, and your app's target SDK is 23 or higher, the app has to list the permissions in the manifest and request those permissions at run time. For more information, see Requesting Permissions at Run Time.

                                                                            Set Up a Location Request

                                                                            To store parameters for requests to the fused location provider, create a LocationRequest. The parameters determine the level of accuracy for location requests. For details of all available location request options, see the LocationRequest class reference. This lesson sets the update interval, fastest update interval, and priority, as described below:

                                                                            Update interval
                                                                            setInterval() - This method sets the rate in milliseconds at which your app prefers to receive location updates. Note that the location updates may be faster than this rate if another app is receiving updates at a faster rate, or slower than this rate, or there may be no updates at all (if the device has no connectivity, for example).
                                                                            Fastest update interval
                                                                            setFastestInterval() - This method sets the fastest rate in milliseconds at which your app can handle location updates. You need to set this rate because other apps also affect the rate at which updates are sent. The Google Play services location APIs send out updates at the fastest rate that any app has requested with setInterval(). If this rate is faster than your app can handle, you may encounter problems with UI flicker or data overflow. To prevent this, call setFastestInterval() to set an upper limit to the update rate.
                                                                            Priority

                                                                            setPriority() - This method sets the priority of the request, which gives the Google Play services location services a strong hint about which location sources to use. The following values are supported:

                                                                            • PRIORITY_BALANCED_POWER_ACCURACY - Use this setting to request location precision to within a city block, which is an accuracy of approximately 100 meters. This is considered a coarse level of accuracy, and is likely to consume less power. With this setting, the location services are likely to use WiFi and cell tower positioning. Note, however, that the choice of location provider depends on many other factors, such as which sources are available.
                                                                            • PRIORITY_HIGH_ACCURACY - Use this setting to request the most precise location possible. With this setting, the location services are more likely to use GPS to determine the location.
                                                                            • PRIORITY_LOW_POWER - Use this setting to request city-level precision, which is an accuracy of approximately 10 kilometers. This is considered a coarse level of accuracy, and is likely to consume less power.
                                                                            • PRIORITY_NO_POWER - Use this setting if you need negligible impact on power consumption, but want to receive location updates when available. With this setting, your app does not trigger any location updates, but receives locations triggered by other apps.

                                                                            Create the location request and set the parameters as shown in this code sample:

                                                                            protected void createLocationRequest() {
                                                                                LocationRequest mLocationRequest = new LocationRequest();
                                                                                mLocationRequest.setInterval(10000);
                                                                                mLocationRequest.setFastestInterval(5000);
                                                                                mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
                                                                            }
                                                                            

                                                                            The priority of PRIORITY_HIGH_ACCURACY, combined with the ACCESS_FINE_LOCATION permission setting that you've defined in the app manifest, and a fast update interval of 5000 milliseconds (5 seconds), causes the fused location provider to return location updates that are accurate to within a few feet. This approach is appropriate for mapping apps that display the location in real time.

                                                                            Performance hint: If your app accesses the network or does other long-running work after receiving a location update, adjust the fastest interval to a slower value. This adjustment prevents your app from receiving updates it can't use. Once the long-running work is done, set the fastest interval back to a fast value.

                                                                            Get Current Location Settings

                                                                            Once you have connected to Google Play services and the location services API, you can get the current location settings of a user's device. To do this, create a LocationSettingsRequest.Builder, and add one or more location requests. The following code snippet shows how to add the location request that was created in the previous step:

                                                                            LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                                                                                 .addLocationRequest(mLocationRequest);
                                                                            

                                                                            Next check whether the current location settings are satisfied:

                                                                            PendingResult<LocationSettingsResult> result =
                                                                                     LocationServices.SettingsApi.checkLocationSettings(mGoogleClient,
                                                                                             builder.build());

                                                                            When the PendingResult returns, your app can check the location settings by looking at the status code from the LocationSettingsResult object. To get even more details about the the current state of the relevant location settings, your app can call the LocationSettingsResult object's getLocationSettingsStates() method.

                                                                            Prompt the User to Change Location Settings

                                                                            To determine whether the location settings are appropriate for the location request, check the status code from the LocationSettingsResult object. A status code of RESOLUTION_REQUIRED indicates that the settings must be changed. To prompt the user for permission to modify the location settings, call startResolutionForResult(Activity, int). This method brings up a dialog asking for the user's permission to modify location settings. The following code snippet shows how to check the location settings, and how to call startResolutionForResult(Activity, int).

                                                                            result.setResultCallback(new ResultCallback<LocationSettingsResult>()) {
                                                                                 @Override
                                                                                 public void onResult(LocationSettingsResult result) {
                                                                                     final Status status = result.getStatus();
                                                                                     final LocationSettingsStates = result.getLocationSettingsStates();
                                                                                     switch (status.getStatusCode()) {
                                                                                         case LocationSettingsStatusCodes.SUCCESS:
                                                                                             // All location settings are satisfied. The client can
                                                                                             // initialize location requests here.
                                                                                             ...
                                                                                             break;
                                                                                         case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                                                                                             // Location settings are not satisfied, but this can be fixed
                                                                                             // by showing the user a dialog.
                                                                                             try {
                                                                                                 // Show the dialog by calling startResolutionForResult(),
                                                                                                 // and check the result in onActivityResult().
                                                                                                 status.startResolutionForResult(
                                                                                                     OuterClass.this,
                                                                                                     REQUEST_CHECK_SETTINGS);
                                                                                             } catch (SendIntentException e) {
                                                                                                 // Ignore the error.
                                                                                             }
                                                                                             break;
                                                                                         case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                                                                                             // Location settings are not satisfied. However, we have no way
                                                                                             // to fix the settings so we won't show the dialog.
                                                                                             ...
                                                                                             break;
                                                                                     }
                                                                                 }
                                                                             });

                                                                            The next lesson, Receiving Location Updates, shows you how to receive periodic location updates.

                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                            This class requires API level or higher

                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                            Receiving Location Updates | Android Developers Skip to content

                                                                            Most visited

                                                                            Recently visited

                                                                            navigation

                                                                              Receiving Location Updates

                                                                              If your app can continuously track location, it can deliver more relevant information to the user. For example, if your app helps the user find their way while walking or driving, or if your app tracks the location of assets, it needs to get the location of the device at regular intervals. As well as the geographical location (latitude and longitude), you may want to give the user further information such as the bearing (horizontal direction of travel), altitude, or velocity of the device. This information, and more, is available in the Location object that your app can retrieve from the fused location provider.

                                                                              While you can get a device's location with getLastLocation(), as illustrated in the lesson on Getting the Last Known Location, a more direct approach is to request periodic updates from the fused location provider. In response, the API updates your app periodically with the best available location, based on the currently-available location providers such as WiFi and GPS (Global Positioning System). The accuracy of the location is determined by the providers, the location permissions you've requested, and the options you set in the location request.

                                                                              This lesson shows you how to request regular updates about a device's location using the requestLocationUpdates() method in the fused location provider.

                                                                              Get the Last Known Location

                                                                              The last known location of the device provides a handy base from which to start, ensuring that the app has a known location before starting the periodic location updates. The lesson on Getting the Last Known Location shows you how to get the last known location by calling getLastLocation(). The snippets in the following sections assume that your app has already retrieved the last known location and stored it as a Location object in the global variable mCurrentLocation.

                                                                              Apps that use location services must request location permissions. In this lesson you require fine location detection, so that your app can get as precise a location as possible from the available location providers. Request this permission with the uses-permission element in your app manifest, as shown in the following example:

                                                                              <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                  package="com.google.android.gms.location.sample.locationupdates" >
                                                                              
                                                                                <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
                                                                              </manifest>
                                                                              

                                                                              Request Location Updates

                                                                              Before requesting location updates, your app must connect to location services and make a location request. The lesson on Changing Location Settings shows you how to do this. Once a location request is in place you can start the regular updates by calling requestLocationUpdates(). Do this in the onConnected() callback provided by Google API Client, which is called when the client is ready.

                                                                              Depending on the form of the request, the fused location provider either invokes the LocationListener.onLocationChanged() callback method and passes it a Location object, or issues a PendingIntent that contains the location in its extended data. The accuracy and frequency of the updates are affected by the location permissions you've requested and the options you set in the location request object.

                                                                              This lesson shows you how to get the update using the LocationListener callback approach. Call requestLocationUpdates(), passing it your instance of the GoogleApiClient, the LocationRequest object, and a LocationListener. Define a startLocationUpdates() method, called from the onConnected() callback, as shown in the following code sample:

                                                                              @Override
                                                                              public void onConnected(Bundle connectionHint) {
                                                                                  ...
                                                                                  if (mRequestingLocationUpdates) {
                                                                                      startLocationUpdates();
                                                                                  }
                                                                              }
                                                                              
                                                                              protected void startLocationUpdates() {
                                                                                  LocationServices.FusedLocationApi.requestLocationUpdates(
                                                                                          mGoogleApiClient, mLocationRequest, this);
                                                                              }
                                                                              

                                                                              Notice that the above code snippet refers to a boolean flag, mRequestingLocationUpdates, used to track whether the user has turned location updates on or off. For more about retaining the value of this flag across instances of the activity, see Save the State of the Activity.

                                                                              Define the Location Update Callback

                                                                              The fused location provider invokes the LocationListener.onLocationChanged() callback method. The incoming argument is a Location object containing the location's latitude and longitude. The following snippet shows how to implement the LocationListener interface and define the method, then get the timestamp of the location update and display the latitude, longitude and timestamp on your app's user interface:

                                                                              public class MainActivity extends ActionBarActivity implements
                                                                                      ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
                                                                                  ...
                                                                                  @Override
                                                                                  public void onLocationChanged(Location location) {
                                                                                      mCurrentLocation = location;
                                                                                      mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
                                                                                      updateUI();
                                                                                  }
                                                                              
                                                                                  private void updateUI() {
                                                                                      mLatitudeTextView.setText(String.valueOf(mCurrentLocation.getLatitude()));
                                                                                      mLongitudeTextView.setText(String.valueOf(mCurrentLocation.getLongitude()));
                                                                                      mLastUpdateTimeTextView.setText(mLastUpdateTime);
                                                                                  }
                                                                              }
                                                                              

                                                                              Stop Location Updates

                                                                              Consider whether you want to stop the location updates when the activity is no longer in focus, such as when the user switches to another app or to a different activity in the same app. This can be handy to reduce power consumption, provided the app doesn't need to collect information even when it's running in the background. This section shows how you can stop the updates in the activity's onPause() method.

                                                                              To stop location updates, call removeLocationUpdates(), passing it your instance of the GoogleApiClient object and a LocationListener, as shown in the following code sample:

                                                                              @Override
                                                                              protected void onPause() {
                                                                                  super.onPause();
                                                                                  stopLocationUpdates();
                                                                              }
                                                                              
                                                                              protected void stopLocationUpdates() {
                                                                                  LocationServices.FusedLocationApi.removeLocationUpdates(
                                                                                          mGoogleApiClient, this);
                                                                              }
                                                                              

                                                                              Use a boolean, mRequestingLocationUpdates, to track whether location updates are currently turned on. In the activity's onResume() method, check whether location updates are currently active, and activate them if not:

                                                                              @Override
                                                                              public void onResume() {
                                                                                  super.onResume();
                                                                                  if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) {
                                                                                      startLocationUpdates();
                                                                                  }
                                                                              }
                                                                              

                                                                              Save the State of the Activity

                                                                              A change to the device's configuration, such as a change in screen orientation or language, can cause the current activity to be destroyed. Your app must therefore store any information it needs to recreate the activity. One way to do this is via an instance state stored in a Bundle object.

                                                                              The following code sample shows how to use the activity's onSaveInstanceState() callback to save the instance state:

                                                                              public void onSaveInstanceState(Bundle savedInstanceState) {
                                                                                  savedInstanceState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY,
                                                                                          mRequestingLocationUpdates);
                                                                                  savedInstanceState.putParcelable(LOCATION_KEY, mCurrentLocation);
                                                                                  savedInstanceState.putString(LAST_UPDATED_TIME_STRING_KEY, mLastUpdateTime);
                                                                                  super.onSaveInstanceState(savedInstanceState);
                                                                              }
                                                                              

                                                                              Define an updateValuesFromBundle() method to restore the saved values from the previous instance of the activity, if they're available. Call the method from the activity's onCreate() method, as shown in the following code sample:

                                                                              @Override
                                                                              public void onCreate(Bundle savedInstanceState) {
                                                                                  ...
                                                                                  updateValuesFromBundle(savedInstanceState);
                                                                              }
                                                                              
                                                                              private void updateValuesFromBundle(Bundle savedInstanceState) {
                                                                                  if (savedInstanceState != null) {
                                                                                      // Update the value of mRequestingLocationUpdates from the Bundle, and
                                                                                      // make sure that the Start Updates and Stop Updates buttons are
                                                                                      // correctly enabled or disabled.
                                                                                      if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
                                                                                          mRequestingLocationUpdates = savedInstanceState.getBoolean(
                                                                                                  REQUESTING_LOCATION_UPDATES_KEY);
                                                                                          setButtonsEnabledState();
                                                                                      }
                                                                              
                                                                                      // Update the value of mCurrentLocation from the Bundle and update the
                                                                                      // UI to show the correct latitude and longitude.
                                                                                      if (savedInstanceState.keySet().contains(LOCATION_KEY)) {
                                                                                          // Since LOCATION_KEY was found in the Bundle, we can be sure that
                                                                                          // mCurrentLocationis not null.
                                                                                          mCurrentLocation = savedInstanceState.getParcelable(LOCATION_KEY);
                                                                                      }
                                                                              
                                                                                      // Update the value of mLastUpdateTime from the Bundle and update the UI.
                                                                                      if (savedInstanceState.keySet().contains(LAST_UPDATED_TIME_STRING_KEY)) {
                                                                                          mLastUpdateTime = savedInstanceState.getString(
                                                                                                  LAST_UPDATED_TIME_STRING_KEY);
                                                                                      }
                                                                                      updateUI();
                                                                                  }
                                                                              }
                                                                              

                                                                              For more about saving instance state, see the Android Activity class reference.

                                                                              Note: For a more persistent storage, you can store the user's preferences in your app's SharedPreferences. Set the shared preference in your activity's onPause() method, and retrieve the preference in onResume(). For more information about saving preferences, read Saving Key-Value Sets.

                                                                              The next lesson, Displaying a Location Address, shows you how to display the street address for a given location.

                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                              This class requires API level or higher

                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                              Displaying a Location Address | Android Developers Skip to content

                                                                              Most visited

                                                                              Recently visited

                                                                              navigation

                                                                                Displaying a Location Address

                                                                                The lessons Getting the Last Known Location and Receiving Location Updates describe how to get the user's location in the form of a Location object that contains latitude and longitude coordinates. Although latitude and longitude are useful for calculating distance or displaying a map position, in many cases the address of the location is more useful. For example, if you want to let your users know where they are or what is close by, a street address is more meaningful than the geographic coordinates (latitude/longitude) of the location.

                                                                                Using the Geocoder class in the Android framework location APIs, you can convert an address to the corresponding geographic coordinates. This process is called geocoding. Alternatively, you can convert a geographic location to an address. The address lookup feature is also known as reverse geocoding.

                                                                                This lesson shows you how to use the getFromLocation() method to convert a geographic location to an address. The method returns an estimated street address corresponding to a given latitude and longitude.

                                                                                Get a Geographic Location

                                                                                The last known location of the device is a useful starting point for the address lookup feature. The lesson on Getting the Last Known Location shows you how to use the getLastLocation() method provided by the fused location provider to find the latest location of the device.

                                                                                To access the fused location provider, you need to create an instance of the Google Play services API client. To learn how to connect your client, see Connect to Google Play Services.

                                                                                In order for the fused location provider to retrieve a precise street address, set the location permission in your app manifest to ACCESS_FINE_LOCATION, as shown in the following example:

                                                                                <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                    package="com.google.android.gms.location.sample.locationupdates" >
                                                                                
                                                                                  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
                                                                                </manifest>
                                                                                

                                                                                Define an Intent Service to Fetch the Address

                                                                                The getFromLocation() method provided by the Geocoder class accepts a latitude and longitude, and returns a list of addresses. The method is synchronous, and may take a long time to do its work, so you should not call it from the main, user interface (UI) thread of your app.

                                                                                The IntentService class provides a structure for running a task on a background thread. Using this class, you can handle a long-running operation without affecting your UI's responsiveness. Note that the AsyncTask class also allows you to perform background operations, but it's designed for short operations. An AsyncTask shouldn't keep a reference to the UI if the activity is recreated, for example when the device is rotated. In contrast, an IntentService doesn't need to be cancelled when the activity is rebuilt.

                                                                                Define a FetchAddressIntentService class that extends IntentService. This class is your address lookup service. The intent service handles an intent asynchronously on a worker thread, and stops itself when it runs out of work. The intent extras provide the data needed by the service, including a Location object for conversion to an address, and a ResultReceiver object to handle the results of the address lookup. The service uses a Geocoder to fetch the address for the location, and sends the results to the ResultReceiver.

                                                                                Define the Intent Service in your App Manifest

                                                                                Add an entry to your app manifest defining the intent service:

                                                                                <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                    package="com.google.android.gms.location.sample.locationaddress" >
                                                                                    <application
                                                                                        ...
                                                                                        <service
                                                                                            android:name=".FetchAddressIntentService"
                                                                                            android:exported="false"/>
                                                                                    </application>
                                                                                    ...
                                                                                </manifest>
                                                                                

                                                                                Note: The <service> element in the manifest doesn't need to include an intent filter, because your main activity creates an explicit intent by specifying the name of the class to use for the intent.

                                                                                Create a Geocoder

                                                                                The process of converting a geographic location to an address is called reverse geocoding. To perform the main work of the intent service, that is, your reverse geocoding request, implement onHandleIntent() within the FetchAddressIntentService class. Create a Geocoder object to handle the reverse geocoding.

                                                                                A locale represents a specific geographical or linguistic region. Locale objects are used to adjust the presentation of information, such as numbers or dates, to suit the conventions in the region represented by the locale. Pass a Locale object to the Geocoder object, to ensure that the resulting address is localized to the user's geographic region.

                                                                                @Override
                                                                                protected void onHandleIntent(Intent intent) {
                                                                                    Geocoder geocoder = new Geocoder(this, Locale.getDefault());
                                                                                    ...
                                                                                }
                                                                                

                                                                                Retrieve the street address data

                                                                                The next step is to retrieve the street address from the geocoder, handle any errors that may occur, and send the results back to the activity that requested the address. To report the results of the geocoding process, you need two numeric constants that indicate success or failure. Define a Constants class to contain the values, as shown in this code snippet:

                                                                                public final class Constants {
                                                                                    public static final int SUCCESS_RESULT = 0;
                                                                                    public static final int FAILURE_RESULT = 1;
                                                                                    public static final String PACKAGE_NAME =
                                                                                        "com.google.android.gms.location.sample.locationaddress";
                                                                                    public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER";
                                                                                    public static final String RESULT_DATA_KEY = PACKAGE_NAME +
                                                                                        ".RESULT_DATA_KEY";
                                                                                    public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME +
                                                                                        ".LOCATION_DATA_EXTRA";
                                                                                }
                                                                                

                                                                                To get a street address corresponding to a geographical location, call getFromLocation(), passing it the latitude and longitude from the location object, and the maximum number of addresses you want returned. In this case, you want just one address. The geocoder returns an array of addresses. If no addresses were found to match the given location, it returns an empty list. If there is no backend geocoding service available, the geocoder returns null.

                                                                                Check for the following errors as shown in the code sample below. If an error occurs, place the corresponding error message in the errorMessage variable, so you can send it back to the requesting activity:

                                                                                • No location data provided - The intent extras do not include the Location object required for reverse geocoding.
                                                                                • Invalid latitude or longitude used - The latitude and/or longitude values provided in the Location object are invalid.
                                                                                • No geocoder available - The background geocoding service is not available, due to a network error or IO exception.
                                                                                • Sorry, no address found - The geocoder could not find an address for the given latitude/longitude.

                                                                                To get the individual lines of an address object, use the getAddressLine() method provided by the Address class. Then join the lines into a list of address fragments ready to return to the activity that requested the address.

                                                                                To send the results back to the requesting activity, call the deliverResultToReceiver() method (defined in Return the address to the requestor). The results consist of the previously-mentioned numeric success/failure code and a string. In the case of a successful reverse geocoding, the string contains the address. In the case of a failure, the string contains the error message, as shown in the code sample below:

                                                                                @Override
                                                                                protected void onHandleIntent(Intent intent) {
                                                                                    String errorMessage = "";
                                                                                
                                                                                    // Get the location passed to this service through an extra.
                                                                                    Location location = intent.getParcelableExtra(
                                                                                            Constants.LOCATION_DATA_EXTRA);
                                                                                
                                                                                    ...
                                                                                
                                                                                    List<Address> addresses = null;
                                                                                
                                                                                    try {
                                                                                        addresses = geocoder.getFromLocation(
                                                                                                location.getLatitude(),
                                                                                                location.getLongitude(),
                                                                                                // In this sample, get just a single address.
                                                                                                1);
                                                                                    } catch (IOException ioException) {
                                                                                        // Catch network or other I/O problems.
                                                                                        errorMessage = getString(R.string.service_not_available);
                                                                                        Log.e(TAG, errorMessage, ioException);
                                                                                    } catch (IllegalArgumentException illegalArgumentException) {
                                                                                        // Catch invalid latitude or longitude values.
                                                                                        errorMessage = getString(R.string.invalid_lat_long_used);
                                                                                        Log.e(TAG, errorMessage + ". " +
                                                                                                "Latitude = " + location.getLatitude() +
                                                                                                ", Longitude = " +
                                                                                                location.getLongitude(), illegalArgumentException);
                                                                                    }
                                                                                
                                                                                    // Handle case where no address was found.
                                                                                    if (addresses == null || addresses.size()  == 0) {
                                                                                        if (errorMessage.isEmpty()) {
                                                                                            errorMessage = getString(R.string.no_address_found);
                                                                                            Log.e(TAG, errorMessage);
                                                                                        }
                                                                                        deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
                                                                                    } else {
                                                                                        Address address = addresses.get(0);
                                                                                        ArrayList<String> addressFragments = new ArrayList<String>();
                                                                                
                                                                                        // Fetch the address lines using getAddressLine,
                                                                                        // join them, and send them to the thread.
                                                                                        for(int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                                                                                            addressFragments.add(address.getAddressLine(i));
                                                                                        }
                                                                                        Log.i(TAG, getString(R.string.address_found));
                                                                                        deliverResultToReceiver(Constants.SUCCESS_RESULT,
                                                                                                TextUtils.join(System.getProperty("line.separator"),
                                                                                                        addressFragments));
                                                                                    }
                                                                                }
                                                                                

                                                                                Return the address to the requestor

                                                                                The final thing the intent service must do is send the address back to a ResultReceiver in the activity that started the service. The ResultReceiver class allows you to send a numeric result code as well as a message containing the result data. The numeric code is useful for reporting the success or failure of the geocoding request. In the case of a successful reverse geocoding, the message contains the address. In the case of a failure, the message contains some text describing the reason for failure.

                                                                                You have already retrieved the address from the geocoder, trapped any errors that may occur, and called the deliverResultToReceiver() method. Now you need to define the deliverResultToReceiver() method that sends a result code and message bundle to the result receiver.

                                                                                For the result code, use the value that you've passed to the deliverResultToReceiver() method in the resultCode parameter. To construct the message bundle, concatenate the RESULT_DATA_KEY constant from your Constants class (defined in Retrieve the street address data) and the value in the message parameter passed to the deliverResultToReceiver() method, as shown in the following sample:

                                                                                public class FetchAddressIntentService extends IntentService {
                                                                                    protected ResultReceiver mReceiver;
                                                                                    ...
                                                                                    private void deliverResultToReceiver(int resultCode, String message) {
                                                                                        Bundle bundle = new Bundle();
                                                                                        bundle.putString(Constants.RESULT_DATA_KEY, message);
                                                                                        mReceiver.send(resultCode, bundle);
                                                                                    }
                                                                                }
                                                                                

                                                                                Start the Intent Service

                                                                                The intent service, as defined in the previous section, runs in the background and is responsible for fetching the address corresponding to a given geographic location. When you start the service, the Android framework instantiates and starts the service if it isn't already running, and creates a process if needed. If the service is already running then it remains running. Because the service extends IntentService, it shuts down automatically when all intents have been processed.

                                                                                Start the service from your app's main activity, and create an Intent to pass data to the service. You need an explicit intent, because you want only your service to respond to the intent. For more information, see Intent Types.

                                                                                To create an explicit intent, specify the name of the class to use for the service: FetchAddressIntentService.class. Pass two pieces of information in the intent extras:

                                                                                • A ResultReceiver to handle the results of the address lookup.
                                                                                • A Location object containing the latitude and longitude that you want to convert to an address.

                                                                                The following code sample shows you how to start the intent service:

                                                                                public class MainActivity extends ActionBarActivity implements
                                                                                        ConnectionCallbacks, OnConnectionFailedListener {
                                                                                
                                                                                    protected Location mLastLocation;
                                                                                    private AddressResultReceiver mResultReceiver;
                                                                                    ...
                                                                                
                                                                                    protected void startIntentService() {
                                                                                        Intent intent = new Intent(this, FetchAddressIntentService.class);
                                                                                        intent.putExtra(Constants.RECEIVER, mResultReceiver);
                                                                                        intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);
                                                                                        startService(intent);
                                                                                    }
                                                                                }
                                                                                

                                                                                Call the above startIntentService() method when the user takes an action that requires a geocoding address lookup. For example, the user may press a Fetch address button on your app's UI. Before starting the intent service, you need to check that the connection to Google Play services is present. The following code snippet shows the call to the startIntentService() method in the button handler:

                                                                                public void fetchAddressButtonHandler(View view) {
                                                                                    // Only start the service to fetch the address if GoogleApiClient is
                                                                                    // connected.
                                                                                    if (mGoogleApiClient.isConnected() && mLastLocation != null) {
                                                                                        startIntentService();
                                                                                    }
                                                                                    // If GoogleApiClient isn't connected, process the user's request by
                                                                                    // setting mAddressRequested to true. Later, when GoogleApiClient connects,
                                                                                    // launch the service to fetch the address. As far as the user is
                                                                                    // concerned, pressing the Fetch Address button
                                                                                    // immediately kicks off the process of getting the address.
                                                                                    mAddressRequested = true;
                                                                                    updateUIWidgets();
                                                                                }
                                                                                

                                                                                You must also start the intent service when the connection to Google Play services is established, if the user has already clicked the button on your app's UI. The following code snippet shows the call to the startIntentService() method in the onConnected() callback provided by the Google API Client:

                                                                                public class MainActivity extends ActionBarActivity implements
                                                                                        ConnectionCallbacks, OnConnectionFailedListener {
                                                                                    ...
                                                                                    @Override
                                                                                    public void onConnected(Bundle connectionHint) {
                                                                                        // Gets the best and most recent location currently available,
                                                                                        // which may be null in rare cases when a location is not available.
                                                                                        mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
                                                                                                mGoogleApiClient);
                                                                                
                                                                                        if (mLastLocation != null) {
                                                                                            // Determine whether a Geocoder is available.
                                                                                            if (!Geocoder.isPresent()) {
                                                                                                Toast.makeText(this, R.string.no_geocoder_available,
                                                                                                        Toast.LENGTH_LONG).show();
                                                                                                return;
                                                                                            }
                                                                                
                                                                                            if (mAddressRequested) {
                                                                                                startIntentService();
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                }
                                                                                

                                                                                Receive the Geocoding Results

                                                                                The intent service has handled the geocoding request, and uses a ResultReceiver to return the results to the activity that made the request. In the activity that makes the request, define an AddressResultReceiver that extends ResultReceiver to handle the response from FetchAddressIntentService.

                                                                                The result includes a numeric result code (resultCode) as well as a message containing the result data (resultData). If the reverse geocoding process was successful, the resultData contains the address. In the case of a failure, the resultData contains text describing the reason for failure. For details of the possible errors, see Return the address to the requestor.

                                                                                Override the onReceiveResult() method to handle the results delivered to the result receiver, as shown in the following code sample:

                                                                                public class MainActivity extends ActionBarActivity implements
                                                                                        ConnectionCallbacks, OnConnectionFailedListener {
                                                                                    ...
                                                                                    class AddressResultReceiver extends ResultReceiver {
                                                                                        public AddressResultReceiver(Handler handler) {
                                                                                            super(handler);
                                                                                        }
                                                                                
                                                                                        @Override
                                                                                        protected void onReceiveResult(int resultCode, Bundle resultData) {
                                                                                
                                                                                            // Display the address string
                                                                                            // or an error message sent from the intent service.
                                                                                            mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
                                                                                            displayAddressOutput();
                                                                                
                                                                                            // Show a toast message if an address was found.
                                                                                            if (resultCode == Constants.SUCCESS_RESULT) {
                                                                                                showToast(getString(R.string.address_found));
                                                                                            }
                                                                                
                                                                                        }
                                                                                    }
                                                                                }
                                                                                
                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                This class requires API level or higher

                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                Creating and Monitoring Geofences | Android Developers Skip to content

                                                                                Most visited

                                                                                Recently visited

                                                                                navigation

                                                                                  Creating and Monitoring Geofences

                                                                                  Geofencing combines awareness of the user's current location with awareness of the user's proximity to locations that may be of interest. To mark a location of interest, you specify its latitude and longitude. To adjust the proximity for the location, you add a radius. The latitude, longitude, and radius define a geofence, creating a circular area, or fence, around the location of interest.

                                                                                  You can have multiple active geofences, with a limit of 100 per device user. For each geofence, you can ask Location Services to send you entrance and exit events, or you can specify a duration within the geofence area to wait, or dwell, before triggering an event. You can limit the duration of any geofence by specifying an expiration duration in milliseconds. After the geofence expires, Location Services automatically removes it.

                                                                                  This lesson shows you how to add and remove geofences, and then listen for geofence transitions using an IntentService.

                                                                                  We recommend upgrading existing apps to use the LocationServices class, which contains the GeofencingApi interface. The LocationServices class replaces the LocationClient (deprecated).

                                                                                  Set up for Geofence Monitoring

                                                                                  The first step in requesting geofence monitoring is to request the necessary permission. To use geofencing, your app must request ACCESS_FINE_LOCATION. To request this permission, add the following element as a child element of the <manifest> element in your app manifest:

                                                                                  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
                                                                                  

                                                                                  If you want to use an IntentService to listen for geofence transitions, add an element specifying the service name. This element must be a child of the <application> element:

                                                                                  <application
                                                                                     android:allowBackup="true">
                                                                                     ...
                                                                                     <service android:name=".GeofenceTransitionsIntentService"/>
                                                                                  <application/>
                                                                                  

                                                                                  To access the location APIs, you need to create an instance of the Google Play services API client. To learn how to connect your client, see Connect to Google Play Services.

                                                                                  Create and Add Geofences

                                                                                  Your app needs to create and add geofences using the location API's builder class for creating Geofence objects, and the convenience class for adding them. Also, to handle the intents sent from Location Services when geofence transitions occur, you can define a PendingIntent as shown in this section.

                                                                                  Note: On single-user devices, there is a limit of 100 geofences per app. For multi-user devices, the limit is 100 geofences per app per device user.

                                                                                  Create geofence objects

                                                                                  First, use Geofence.Builder to create a geofence, setting the desired radius, duration, and transition types for the geofence. For example, to populate a list object named mGeofenceList:

                                                                                  mGeofenceList.add(new Geofence.Builder()
                                                                                      // Set the request ID of the geofence. This is a string to identify this
                                                                                      // geofence.
                                                                                      .setRequestId(entry.getKey())
                                                                                  
                                                                                      .setCircularRegion(
                                                                                              entry.getValue().latitude,
                                                                                              entry.getValue().longitude,
                                                                                              Constants.GEOFENCE_RADIUS_IN_METERS
                                                                                      )
                                                                                      .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
                                                                                      .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
                                                                                              Geofence.GEOFENCE_TRANSITION_EXIT)
                                                                                      .build());
                                                                                  

                                                                                  This example pulls data from a constants file. In actual practice, apps might dynamically create geofences based on the user's location.

                                                                                  Specify geofences and initial triggers

                                                                                  The following snippet uses the GeofencingRequest class and its nested GeofencingRequestBuilder class to specify the geofences to monitor and to set how related geofence events are triggered:

                                                                                  private GeofencingRequest getGeofencingRequest() {
                                                                                      GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
                                                                                      builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
                                                                                      builder.addGeofences(mGeofenceList);
                                                                                      return builder.build();
                                                                                  }
                                                                                  

                                                                                  This example shows the use of two geofence triggers. The GEOFENCE_TRANSITION_ENTER transition triggers when a device enters a geofence, and the GEOFENCE_TRANSITION_EXIT transition triggers when a device exits a geofence. Specifying INITIAL_TRIGGER_ENTER tells Location services that GEOFENCE_TRANSITION_ENTER should be triggered if the the device is already inside the geofence.

                                                                                  In many cases, it may be preferable to use instead INITIAL_TRIGGER_DWELL, which triggers events only when the user stops for a defined duration within a geofence. This approach can help reduce "alert spam" resulting from large numbers notifications when a device briefly enters and exits geofences. Another strategy for getting best results from your geofences is to set a minimum radius of 100 meters. This helps account for the location accuracy of typical Wi-Fi networks, and also helps reduce device power consumption.

                                                                                  Define an Intent for geofence transitions

                                                                                  The Intent sent from Location Services can trigger various actions in your app, but you should not have it start an activity or fragment, because components should only become visible in response to a user action. In many cases, an IntentService is a good way to handle the intent. An IntentService can post a notification, do long-running background work, send intents to other services, or send a broadcast intent. The following snippet shows how to define a PendingIntent that starts an IntentService:

                                                                                  public class MainActivity extends FragmentActivity {
                                                                                      ...
                                                                                      private PendingIntent getGeofencePendingIntent() {
                                                                                          // Reuse the PendingIntent if we already have it.
                                                                                          if (mGeofencePendingIntent != null) {
                                                                                              return mGeofencePendingIntent;
                                                                                          }
                                                                                          Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
                                                                                          // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
                                                                                          // calling addGeofences() and removeGeofences().
                                                                                          return PendingIntent.getService(this, 0, intent, PendingIntent.
                                                                                                  FLAG_UPDATE_CURRENT);
                                                                                      }
                                                                                  

                                                                                  Add geofences

                                                                                  To add geofences, use the GeoencingApi.addGeofences() method. Provide the Google API client, the GeofencingRequest object, and the PendingIntent. The following snippet, which processes the results in onResult(), assumes that the main activity implements ResultCallback:

                                                                                  public class MainActivity extends FragmentActivity {
                                                                                      ...
                                                                                      LocationServices.GeofencingApi.addGeofences(
                                                                                                  mGoogleApiClient,
                                                                                                  getGeofencingRequest(),
                                                                                                  getGeofencePendingIntent()
                                                                                          ).setResultCallback(this);
                                                                                  

                                                                                  Handle Geofence Transitions

                                                                                  When Location Services detects that the user has entered or exited a geofence, it sends out the Intent contained in the PendingIntent you included in the request to add geofences. This Intent is received by a service like GeofenceTransitionsIntentService, which obtains the geofencing event from the intent, determines the type of Geofence transition(s), and determines which of the defined geofences was triggered. It then sends a notification as the output.

                                                                                  The following snippet shows how to define an IntentService that posts a notification when a geofence transition occurs. When the user clicks the notification, the app's main activity appears:

                                                                                  public class GeofenceTransitionsIntentService extends IntentService {
                                                                                     ...
                                                                                      protected void onHandleIntent(Intent intent) {
                                                                                          GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
                                                                                          if (geofencingEvent.hasError()) {
                                                                                              String errorMessage = GeofenceErrorMessages.getErrorString(this,
                                                                                                      geofencingEvent.getErrorCode());
                                                                                              Log.e(TAG, errorMessage);
                                                                                              return;
                                                                                          }
                                                                                  
                                                                                          // Get the transition type.
                                                                                          int geofenceTransition = geofencingEvent.getGeofenceTransition();
                                                                                  
                                                                                          // Test that the reported transition was of interest.
                                                                                          if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                                                                                                  geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
                                                                                  
                                                                                              // Get the geofences that were triggered. A single event can trigger
                                                                                              // multiple geofences.
                                                                                              List triggeringGeofences = geofencingEvent.getTriggeringGeofences();
                                                                                  
                                                                                              // Get the transition details as a String.
                                                                                              String geofenceTransitionDetails = getGeofenceTransitionDetails(
                                                                                                      this,
                                                                                                      geofenceTransition,
                                                                                                      triggeringGeofences
                                                                                              );
                                                                                  
                                                                                              // Send notification and log the transition details.
                                                                                              sendNotification(geofenceTransitionDetails);
                                                                                              Log.i(TAG, geofenceTransitionDetails);
                                                                                          } else {
                                                                                              // Log the error.
                                                                                              Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                                                                                                      geofenceTransition));
                                                                                          }
                                                                                      }
                                                                                  

                                                                                  After detecting the transition event via the PendingIntent, this IntentService gets the geofence transition type and tests whether it is one of the events the app uses to trigger notifications -- either GEOFENCE_TRANSITION_ENTER or GEOFENCE_TRANSITION_EXIT in this case. The service then sends a notification and logs the transition details.

                                                                                  Stop Geofence Monitoring

                                                                                  Stopping geofence monitoring when it is no longer needed or desired can help save battery power and CPU cycles on the device. You can stop geofence monitoring in the main activity used to add and remove geofences; removing a geofence stops it immediately. The API provides methods to remove geofences either by request IDs, or by removing geofences associated with a given PendingIntent.

                                                                                  The following snippet removes geofences by PendingIntent, stopping all further notification when the device enters or exits previously added geofences:

                                                                                  LocationServices.GeofencingApi.removeGeofences(
                                                                                              mGoogleApiClient,
                                                                                              // This is the same pending intent that was used in addGeofences().
                                                                                              getGeofencePendingIntent()
                                                                                      ).setResultCallback(this); // Result processed in onResult().
                                                                                  }
                                                                                  

                                                                                  You can combine geofencing with other location-aware features, such as periodic location updates. For more information, see the other lessons in this class.

                                                                                  Use Best Practices for Geofencing

                                                                                  This section outlines recommendations for using geofencing with the location APIs for Android.

                                                                                  Reduce power consumption

                                                                                  You can use the following techniques to optimize power consumption in your apps that use geofencing:

                                                                                  • Set the notification responsiveness to a higher value. Doing so improves power consumption by increasing the latency of geofence alerts. For example, if you set a responsiveness value of five minutes your app only checks for an entrance or exit alert once every five minutes. Setting lower values does not necessarily mean that users will be notified within that time period (for example, if you set a value of 5 seconds it may take a bit longer than that to receive the alert).

                                                                                  • Use a larger geofence radius for locations where a user spends a significant amount of time, such as home or work. While a larger radius doesn't directly reduce power consumption, it reduces the frequency at which the app checks for entrance or exit, effectively lowering overall power consumption.

                                                                                  Choose the optimal radius for your geofence

                                                                                  For best results, the minimium radius of the geofence should be set between 100 - 150 meters. When Wi-Fi is available location accuracy is usually between 20 - 50 meters. When indoor location is available, the accuracy range can be as small as 5 meters. Unless you know indoor location is available inside the geofence, assume that Wi-Fi location accuracy is about 50 meters.

                                                                                  When Wi-Fi location is not available (for example, when you are driving in rural areas) the location accuracy degrades. The accuracy range can be as large as several hundred meters to several kilometers. In cases like this, you should create geofences using a larger radius.

                                                                                  Use the dwell transition type to reduce alert spam

                                                                                  If you receive a large number of alerts when driving briefly past a geofence, the best way to reduce the alerts is to use a transition type of GEOFENCE_TRANSITION_DWELL instead of GEOFENCE_TRANSITION_ENTER. This way, the dwelling alert is sent only when the user stops inside a geofence for a given period of time. You can choose the duration by setting a loitering delay.

                                                                                  Re-register geofences only when required

                                                                                  Registered geofences are kept in the com.google.process.location process owned by the com.google.android.gms package. The app doesn’t need to do anything to handle the following events, because the system restores geofences after these events:

                                                                                  • Google Play services is upgraded.
                                                                                  • Google Play services is killed and restarted by the system due resource restriction.
                                                                                  • The location process crashes.

                                                                                  The app must re-register geofences if they're still needed after the following events, since the system cannot recover the geofences in the following cases:

                                                                                  • The device is rebooted. The app should listen for the device's boot complete action, and then re- register the geofences required.
                                                                                  • The app is uninstalled and re-installed.
                                                                                  • The app's data is cleared.
                                                                                  • Google Play services data is cleared.
                                                                                  • The app has received a GEOFENCE_NOT_AVAILABLE alert. This typically happens after NLP (Android's Network Location Provider) is disabled.

                                                                                  Troubleshoot the Geofence Entrance Event

                                                                                  If geofences are not being triggered when the device enters a geofence (the GEOFENCE_TRANSITION_ENTER alert isn’t triggered), first ensure that your geofences are registered properly as described in this guide.

                                                                                  Here are some possible reasons for alerts not working as expected:

                                                                                  • Accurate location is not available inside your geofence or your geofence is too small. On most devices, the geofence service uses only network location for geofence triggering. The service uses this approach because network location consumes much less power, it takes less time to get discrete locations, and most importantly it’s available indoors. Starting with Google Play services 3.2, the geofence service calculates the overlapping ratio of the location circle and the geofence circle and only generates the entrance alert when the ratio is at least 85% for a bigger geofence or 75% for a smaller geofence. For an exit alert, the ratio threshold used is 15% or 25%. Any ratio between these thresholds makes the geofence service mark the geofence state as INSIDE_LOW_CONFIDENCE or OUTSIDE_LOW_CONFIDENCE and no alert is sent.
                                                                                  • Wi-Fi is turned off on the device. Having Wi-Fi on can significantly improve the location accuracy, so if Wi-Fi is turned off, your application might never get geofence alerts depending on several settings including the radius of the geofence, the device model, or the Android version. Starting from Android 4.3 (API level 18), we added the capability of “Wi-Fi scan only mode” which allows users to disable Wi-Fi but still get good network location. It’s good practice to prompt the user and provide a shortcut for the user to enable Wi-Fi or Wi-Fi scan only mode if both of them are disabled. Use SettingsApi to ensure that the device's system settings are properly configured for optimal location detection.
                                                                                  • There is no reliable network connectivity inside your geofence. If there is no reliable data connection, alerts might not be generated. This is because the geofence service depends on the network location provider which in turn requires a data connection.
                                                                                  • Alerts can be late. The geofence service does not continuously query for location, so expect some latency when receiving alerts. Usually the latency is less than 2 minutes, even less when the device has been moving. If the device has been stationary for a significant period of time, the latency may increase (up to 6 minutes).
                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                  This class requires API level or higher

                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                  Building Apps with Contacts & Sign-In | Android Developers Skip to content

                                                                                  Most visited

                                                                                  Recently visited

                                                                                  navigation

                                                                                    Building Apps with Contacts & Sign-In

                                                                                    These lessons teach you how to include contact information and authenticate users with the same credentials they use for Google. These features allow your app to connect users with people they care about and provide a personalized experience without creating new user accounts.

                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                    This class requires API level or higher

                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                    Accessing Contacts Data | Android Developers Skip to content

                                                                                    Most visited

                                                                                    Recently visited

                                                                                    navigation

                                                                                      Accessing Contacts Data

                                                                                      Dependencies and prerequisites

                                                                                      • Android 2.0 (API Level 5) or higher
                                                                                      • Experience in using Intent objects
                                                                                      • Experience in using content providers

                                                                                      You should also read

                                                                                      Try it out

                                                                                      Download the sample

                                                                                      ContactsList.zip

                                                                                      The Contacts Provider is the central repository of the user's contacts information, including data from contacts apps and social networking apps. In your apps, you can access Contacts Provider information directly by calling ContentResolver methods or by sending intents to a contacts app.

                                                                                      This class focuses on retrieving lists of contacts, displaying the details for a particular contact, and modifying contacts using intents. The basic techniques described here can be extended to perform more complex tasks. In addition, this class helps you understand the overall structure and operation of the Contacts Provider.

                                                                                      Lessons

                                                                                      Retrieving a List of Contacts
                                                                                      Learn how to retrieve a list of contacts for which the data matches all or part of a search string, using the following techniques:
                                                                                      • Match by contact name
                                                                                      • Match any type of contact data
                                                                                      • Match a specific type of contact data, such as a phone number
                                                                                      Retrieving Details for a Contact
                                                                                      Learn how to retrieve the details for a single contact. A contact's details are data such as phone numbers and email addresses. You can retrieve all details, or you can retrieve details of a specific type, such as all email addresses.
                                                                                      Modifying Contacts Using Intents
                                                                                      Learn how to modify a contact by sending an intent to the People app.
                                                                                      Displaying the Quick Contact Badge
                                                                                      Learn how to display the QuickContactBadge widget. When the user clicks the contact badge widget, a dialog opens that displays the contact's details and action buttons for apps that can handle the details. For example, if the contact has an email address, the dialog displays an action button for the default email app.
                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                      This class requires API level or higher

                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                      Retrieving a List of Contacts | Android Developers Skip to content

                                                                                      Most visited

                                                                                      Recently visited

                                                                                      navigation

                                                                                        Retrieving a List of Contacts

                                                                                        This lesson shows you how to retrieve a list of contacts whose data matches all or part of a search string, using the following techniques:

                                                                                        Match contact names
                                                                                        Retrieve a list of contacts by matching the search string to all or part of the contact name data. The Contacts Provider allows multiple instances of the same name, so this technique can return a list of matches.
                                                                                        Match a specific type of data, such as a phone number
                                                                                        Retrieve a list of contacts by matching the search string to a particular type of detail data such as an email address. For example, this technique allows you to list all of the contacts whose email address matches the search string.
                                                                                        Match any type of data
                                                                                        Retrieve a list of contacts by matching the search string to any type of detail data, including name, phone number, street address, email address, and so forth. For example, this technique allows you to accept any type of data for a search string and then list the contacts for which the data matches the string.

                                                                                        Note: All the examples in this lesson use a CursorLoader to retrieve data from the Contacts Provider. A CursorLoader runs its query on a thread that's separate from the UI thread. This ensures that the query doesn't slow down UI response times and cause a poor user experience. For more information, see the Android training class Loading Data in the Background.

                                                                                        Request Permission to Read the Provider

                                                                                        To do any type of search of the Contacts Provider, your app must have READ_CONTACTS permission. To request this, add this <uses-permission> element to your manifest file as a child element of <manifest>:

                                                                                            <uses-permission android:name="android.permission.READ_CONTACTS" />
                                                                                        

                                                                                        Match a Contact by Name and List the Results

                                                                                        This technique tries to match a search string to the name of a contact or contacts in the Contact Provider's ContactsContract.Contacts table. You usually want to display the results in a ListView, to allow the user to choose among the matched contacts.

                                                                                        Define ListView and item layouts

                                                                                        To display the search results in a ListView, you need a main layout file that defines the entire UI including the ListView, and an item layout file that defines one line of the ListView. For example, you could create the main layout file res/layout/contacts_list_view.xml with the following XML:

                                                                                        <?xml version="1.0" encoding="utf-8"?>
                                                                                        <ListView xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                  android:id="@android:id/list"
                                                                                                  android:layout_width="match_parent"
                                                                                                  android:layout_height="match_parent"/>
                                                                                        

                                                                                        This XML uses the built-in Android ListView widget android:id/list.

                                                                                        Define the item layout file contacts_list_item.xml with the following XML:

                                                                                        <?xml version="1.0" encoding="utf-8"?>
                                                                                        <TextView xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                  android:id="@android:id/text1"
                                                                                                  android:layout_width="match_parent"
                                                                                                  android:layout_height="wrap_content"
                                                                                                  android:clickable="true"/>
                                                                                        

                                                                                        This XML uses the built-in Android TextView widget android:text1.

                                                                                        Note: This lesson doesn't describe the UI for getting a search string from the user, because you may want to get the string indirectly. For example, you can give the user an option to search for contacts whose name matches a string in an incoming text message.

                                                                                        The two layout files you've written define a user interface that shows a ListView. The next step is to write code that uses this UI to display a list of contacts.

                                                                                        Define a Fragment that displays the list of contacts

                                                                                        To display the list of contacts, start by defining a Fragment that's loaded by an Activity. Using a Fragment is a more flexible technique, because you can use one Fragment to display the list and a second Fragment to display the details for a contact that the user chooses from the list. Using this approach, you can combine one of the techniques presented in this lesson with one from the lesson Retrieving Details for a Contact.

                                                                                        To learn how to use one or more Fragment objects from an an Activity, read the training class Building a Dynamic UI with Fragments.

                                                                                        To help you write queries against the Contacts Provider, the Android framework provides a contracts class called ContactsContract, which defines useful constants and methods for accessing the provider. When you use this class, you don't have to define your own constants for content URIs, table names, or columns. To use this class, include the following statement:

                                                                                        import android.provider.ContactsContract;
                                                                                        

                                                                                        Since the code uses a CursorLoader to retrieve data from the provider, you must specify that it implements the loader interface LoaderManager.LoaderCallbacks. Also, to help detect which contact the user selects from the list of search results, implement the adapter interface AdapterView.OnItemClickListener. For example:

                                                                                        ...
                                                                                        import android.support.v4.app.Fragment;
                                                                                        import android.support.v4.app.LoaderManager.LoaderCallbacks;
                                                                                        import android.widget.AdapterView;
                                                                                        ...
                                                                                        public class ContactsFragment extends Fragment implements
                                                                                                LoaderManager.LoaderCallbacks<Cursor>,
                                                                                                AdapterView.OnItemClickListener {
                                                                                        

                                                                                        Define global variables

                                                                                        Define global variables that are used in other parts of the code:

                                                                                            ...
                                                                                            /*
                                                                                             * Defines an array that contains column names to move from
                                                                                             * the Cursor to the ListView.
                                                                                             */
                                                                                            @SuppressLint("InlinedApi")
                                                                                            private final static String[] FROM_COLUMNS = {
                                                                                                    Build.VERSION.SDK_INT
                                                                                                            >= Build.VERSION_CODES.HONEYCOMB ?
                                                                                                            Contacts.DISPLAY_NAME_PRIMARY :
                                                                                                            Contacts.DISPLAY_NAME
                                                                                            };
                                                                                            /*
                                                                                             * Defines an array that contains resource ids for the layout views
                                                                                             * that get the Cursor column contents. The id is pre-defined in
                                                                                             * the Android framework, so it is prefaced with "android.R.id"
                                                                                             */
                                                                                            private final static int[] TO_IDS = {
                                                                                                   android.R.id.text1
                                                                                            };
                                                                                            // Define global mutable variables
                                                                                            // Define a ListView object
                                                                                            ListView mContactsList;
                                                                                            // Define variables for the contact the user selects
                                                                                            // The contact's _ID value
                                                                                            long mContactId;
                                                                                            // The contact's LOOKUP_KEY
                                                                                            String mContactKey;
                                                                                            // A content URI for the selected contact
                                                                                            Uri mContactUri;
                                                                                            // An adapter that binds the result Cursor to the ListView
                                                                                            private SimpleCursorAdapter mCursorAdapter;
                                                                                            ...
                                                                                        

                                                                                        Note: Since Contacts.DISPLAY_NAME_PRIMARY requires Android 3.0 (API version 11) or later, setting your app's minSdkVersion to 10 or below generates an Android Lint warning in Android Studio. To turn off this warning, add the annotation @SuppressLint("InlinedApi") before the definition of FROM_COLUMNS.

                                                                                        Initialize the Fragment

                                                                                        Initialize the Fragment. Add the empty, public constructor required by the Android system, and inflate the Fragment object's UI in the callback method onCreateView(). For example:

                                                                                            // Empty public constructor, required by the system
                                                                                            public ContactsFragment() {}
                                                                                        
                                                                                            // A UI Fragment must inflate its View
                                                                                            @Override
                                                                                            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                                                                                    Bundle savedInstanceState) {
                                                                                                // Inflate the fragment layout
                                                                                                return inflater.inflate(R.layout.contact_list_fragment,
                                                                                                    container, false);
                                                                                            }
                                                                                        

                                                                                        Set up the CursorAdapter for the ListView

                                                                                        Set up the SimpleCursorAdapter that binds the results of the search to the ListView. To get the ListView object that displays the contacts, you need to call Activity.findViewById() using the parent activity of the Fragment. Use the Context of the parent activity when you call setAdapter(). For example:

                                                                                            public void onActivityCreated(Bundle savedInstanceState) {
                                                                                                super.onActivityCreated(savedInstanceState);
                                                                                                ...
                                                                                                // Gets the ListView from the View list of the parent activity
                                                                                                mContactsList =
                                                                                                    (ListView) getActivity().findViewById(R.layout.contact_list_view);
                                                                                                // Gets a CursorAdapter
                                                                                                mCursorAdapter = new SimpleCursorAdapter(
                                                                                                        getActivity(),
                                                                                                        R.layout.contact_list_item,
                                                                                                        null,
                                                                                                        FROM_COLUMNS, TO_IDS,
                                                                                                        0);
                                                                                                // Sets the adapter for the ListView
                                                                                                mContactsList.setAdapter(mCursorAdapter);
                                                                                            }
                                                                                        

                                                                                        Set the selected contact listener

                                                                                        When you display the results of a search, you usually want to allow the user to select a single contact for further processing. For example, when the user clicks a contact you can display the contact's address on a map. To provide this feature, you first defined the current Fragment as the click listener by specifying that the class implements AdapterView.OnItemClickListener, as shown in the section Define a Fragment that displays the list of contacts.

                                                                                        To continue setting up the listener, bind it to the ListView by calling the method setOnItemClickListener() in onActivityCreated(). For example:

                                                                                            public void onActivityCreated(Bundle savedInstanceState) {
                                                                                                ...
                                                                                                // Set the item click listener to be the current fragment.
                                                                                                mContactsList.setOnItemClickListener(this);
                                                                                                ...
                                                                                            }
                                                                                        

                                                                                        Since you specified that the current Fragment is the OnItemClickListener for the ListView, you now need to implement its required method onItemClick(), which handles the click event. This is described in a succeeding section.

                                                                                        Define a projection

                                                                                        Define a constant that contains the columns you want to return from your query. Each item in the ListView displays the contact's display name, which contains the main form of the contact's name. In Android 3.0 (API version 11) and later, the name of this column is Contacts.DISPLAY_NAME_PRIMARY; in versions previous to that, its name is Contacts.DISPLAY_NAME.

                                                                                        The column Contacts._ID is used by the SimpleCursorAdapter binding process. Contacts._ID and LOOKUP_KEY are used together to construct a content URI for the contact the user selects.

                                                                                        ...
                                                                                        @SuppressLint("InlinedApi")
                                                                                        private static final String[] PROJECTION =
                                                                                                {
                                                                                                    Contacts._ID,
                                                                                                    Contacts.LOOKUP_KEY,
                                                                                                    Build.VERSION.SDK_INT
                                                                                                            >= Build.VERSION_CODES.HONEYCOMB ?
                                                                                                            Contacts.DISPLAY_NAME_PRIMARY :
                                                                                                            Contacts.DISPLAY_NAME
                                                                                        
                                                                                                };
                                                                                        

                                                                                        Define constants for the Cursor column indexes

                                                                                        To get data from an individual column in a Cursor, you need the column's index within the Cursor. You can define constants for the indexes of the Cursor columns, because the indexes are the same as the order of the column names in your projection. For example:

                                                                                        // The column index for the _ID column
                                                                                        private static final int CONTACT_ID_INDEX = 0;
                                                                                        // The column index for the LOOKUP_KEY column
                                                                                        private static final int LOOKUP_KEY_INDEX = 1;
                                                                                        

                                                                                        Specify the selection criteria

                                                                                        To specify the data you want, create a combination of text expressions and variables that tell the provider the data columns to search and the values to find.

                                                                                        For the text expression, define a constant that lists the search columns. Although this expression can contain values as well, the preferred practice is to represent the values with a "?" placeholder. During retrieval, the placeholder is replaced with values from an array. Using "?" as a placeholder ensures that the search specification is generated by binding rather than by SQL compilation. This practice eliminates the possibility of malicious SQL injection. For example:

                                                                                            // Defines the text expression
                                                                                            @SuppressLint("InlinedApi")
                                                                                            private static final String SELECTION =
                                                                                                    Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                                                                                                    Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
                                                                                                    Contacts.DISPLAY_NAME + " LIKE ?";
                                                                                            // Defines a variable for the search string
                                                                                            private String mSearchString;
                                                                                            // Defines the array to hold values that replace the ?
                                                                                            private String[] mSelectionArgs = { mSearchString };
                                                                                        

                                                                                        Define the onItemClick() method

                                                                                        In a previous section, you set the item click listener for the ListView. Now implement the action for the listener by defining the method AdapterView.OnItemClickListener.onItemClick():

                                                                                            @Override
                                                                                            public void onItemClick(
                                                                                                AdapterView<?> parent, View item, int position, long rowID) {
                                                                                                // Get the Cursor
                                                                                                Cursor cursor = parent.getAdapter().getCursor();
                                                                                                // Move to the selected contact
                                                                                                cursor.moveToPosition(position);
                                                                                                // Get the _ID value
                                                                                                mContactId = getLong(CONTACT_ID_INDEX);
                                                                                                // Get the selected LOOKUP KEY
                                                                                                mContactKey = getString(CONTACT_KEY_INDEX);
                                                                                                // Create the contact's content Uri
                                                                                                mContactUri = Contacts.getLookupUri(mContactId, mContactKey);
                                                                                                /*
                                                                                                 * You can use mContactUri as the content URI for retrieving
                                                                                                 * the details for a contact.
                                                                                                 */
                                                                                            }
                                                                                        

                                                                                        Initialize the loader

                                                                                        Since you're using a CursorLoader to retrieve data, you must initialize the background thread and other variables that control asynchronous retrieval. Do the initialization in onActivityCreated(), which is invoked immediately before the Fragment UI appears, as shown in the following example:

                                                                                        public class ContactsFragment extends Fragment implements
                                                                                                LoaderManager.LoaderCallbacks<Cursor> {
                                                                                            ...
                                                                                            // Called just before the Fragment displays its UI
                                                                                            @Override
                                                                                            public void onActivityCreated(Bundle savedInstanceState) {
                                                                                                // Always call the super method first
                                                                                                super.onActivityCreated(savedInstanceState);
                                                                                                ...
                                                                                                // Initializes the loader
                                                                                                getLoaderManager().initLoader(0, null, this);
                                                                                        

                                                                                        Implement onCreateLoader()

                                                                                        Implement the method onCreateLoader(), which is called by the loader framework immediately after you call initLoader().

                                                                                        In onCreateLoader(), set up the search string pattern. To make a string into a pattern, insert "%" (percent) characters to represent a sequence of zero or more characters, or "_" (underscore) characters to represent a single character, or both. For example, the pattern "%Jefferson%" would match both "Thomas Jefferson" and "Jefferson Davis".

                                                                                        Return a new CursorLoader from the method. For the content URI, use Contacts.CONTENT_URI. This URI refers to the entire table, as shown in the following example:

                                                                                            ...
                                                                                            @Override
                                                                                            public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
                                                                                                /*
                                                                                                 * Makes search string into pattern and
                                                                                                 * stores it in the selection array
                                                                                                 */
                                                                                                mSelectionArgs[0] = "%" + mSearchString + "%";
                                                                                                // Starts the query
                                                                                                return new CursorLoader(
                                                                                                        getActivity(),
                                                                                                        Contacts.CONTENT_URI,
                                                                                                        PROJECTION,
                                                                                                        SELECTION,
                                                                                                        mSelectionArgs,
                                                                                                        null
                                                                                                );
                                                                                            }
                                                                                        

                                                                                        Implement onLoadFinished() and onLoaderReset()

                                                                                        Implement the onLoadFinished() method. The loader framework calls onLoadFinished() when the Contacts Provider returns the results of the query. In this method, put the result Cursor in the SimpleCursorAdapter. This automatically updates the ListView with the search results:

                                                                                            public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
                                                                                                // Put the result Cursor in the adapter for the ListView
                                                                                                mCursorAdapter.swapCursor(cursor);
                                                                                            }
                                                                                        

                                                                                        The method onLoaderReset() is invoked when the loader framework detects that the result Cursor contains stale data. Delete the SimpleCursorAdapter reference to the existing Cursor. If you don't, the loader framework will not recycle the Cursor, which causes a memory leak. For example:

                                                                                            @Override
                                                                                            public void onLoaderReset(Loader<Cursor> loader) {
                                                                                                // Delete the reference to the existing Cursor
                                                                                                mCursorAdapter.swapCursor(null);
                                                                                        
                                                                                            }
                                                                                        

                                                                                        You now have the key pieces of an app that matches a search string to contact names and returns the result in a ListView. The user can click a contact name to select it. This triggers a listener, in which you can work further with the contact's data. For example, you can retrieve the contact's details. To learn how to do this, continue with the next lesson, Retrieving Details for a Contact.

                                                                                        To learn more about search user interfaces, read the API guide Creating a Search Interface.

                                                                                        The remaining sections in this lesson demonstrate other ways of finding contacts in the Contacts Provider.

                                                                                        Match a Contact By a Specific Type of Data

                                                                                        This technique allows you to specify the type of data you want to match. Retrieving by name is a specific example of this type of query, but you can also do it for any of the types of detail data associated with a contact. For example, you can retrieve contacts that have a specific postal code; in this case, the search string has to match data stored in a postal code row.

                                                                                        To implement this type of retrieval, first implement the following code, as listed in previous sections:

                                                                                        • Request Permission to Read the Provider.
                                                                                        • Define ListView and item layouts.
                                                                                        • Define a Fragment that displays the list of contacts.
                                                                                        • Define global variables.
                                                                                        • Initialize the Fragment.
                                                                                        • Set up the CursorAdapter for the ListView.
                                                                                        • Set the selected contact listener.
                                                                                        • Define constants for the Cursor column indexes.

                                                                                          Although you're retrieving data from a different table, the order of the columns in the projection is the same, so you can use the same indexes for the Cursor.

                                                                                        • Define the onItemClick() method.
                                                                                        • Initialize the loader.
                                                                                        • Implement onLoadFinished() and onLoaderReset().

                                                                                        The following steps show you the additional code you need to match a search string to a particular type of detail data and display the results.

                                                                                        Choose the data type and table

                                                                                        To search for a particular type of detail data, you have to know the custom MIME type value for the data type. Each data type has a unique MIME type value defined by a constant CONTENT_ITEM_TYPE in the subclass of ContactsContract.CommonDataKinds associated with the data type. The subclasses have names that indicate their data type; for example, the subclass for email data is ContactsContract.CommonDataKinds.Email, and the custom MIME type for email data is defined by the constant Email.CONTENT_ITEM_TYPE.

                                                                                        Use the ContactsContract.Data table for your search. All of the constants you need for your projection, selection clause, and sort order are defined in or inherited by this table.

                                                                                        Define a projection

                                                                                        To define a projection, choose one or more of the columns defined in ContactsContract.Data or the classes from which it inherits. The Contacts Provider does an implicit join between ContactsContract.Data and other tables before it returns rows. For example:

                                                                                            @SuppressLint("InlinedApi")
                                                                                            private static final String[] PROJECTION =
                                                                                                {
                                                                                                    /*
                                                                                                     * The detail data row ID. To make a ListView work,
                                                                                                     * this column is required.
                                                                                                     */
                                                                                                    Data._ID,
                                                                                                    // The primary display name
                                                                                                    Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                                                                                                            Data.DISPLAY_NAME_PRIMARY :
                                                                                                            Data.DISPLAY_NAME,
                                                                                                    // The contact's _ID, to construct a content URI
                                                                                                    Data.CONTACT_ID
                                                                                                    // The contact's LOOKUP_KEY, to construct a content URI
                                                                                                    Data.LOOKUP_KEY (a permanent link to the contact
                                                                                                };
                                                                                        

                                                                                        Define search criteria

                                                                                        To search for a string within a particular type of data, construct a selection clause from the following:

                                                                                        • The name of the column that contains your search string. This name varies by data type, so you need to find the subclass of ContactsContract.CommonDataKinds that corresponds to the data type and then choose the column name from that subclass. For example, to search for email addresses, use the column Email.ADDRESS.
                                                                                        • The search string itself, represented as the "?" character in the selection clause.
                                                                                        • The name of the column that contains the custom MIME type value. This name is always Data.MIMETYPE.
                                                                                        • The custom MIME type value for the data type. As described previously, this is the constant CONTENT_ITEM_TYPE in the ContactsContract.CommonDataKinds subclass. For example, the MIME type value for email data is Email.CONTENT_ITEM_TYPE. Enclose the value in single quotes by concatenating a "'" (single quote) character to the start and end of the constant; otherwise, the provider interprets the value as a variable name rather than as a string value. You don't need to use a placeholder for this value, because you're using a constant rather than a user-supplied value.

                                                                                        For example:

                                                                                            /*
                                                                                             * Constructs search criteria from the search string
                                                                                             * and email MIME type
                                                                                             */
                                                                                            private static final String SELECTION =
                                                                                                    /*
                                                                                                     * Searches for an email address
                                                                                                     * that matches the search string
                                                                                                     */
                                                                                                    Email.ADDRESS + " LIKE ? " + "AND " +
                                                                                                    /*
                                                                                                     * Searches for a MIME type that matches
                                                                                                     * the value of the constant
                                                                                                     * Email.CONTENT_ITEM_TYPE. Note the
                                                                                                     * single quotes surrounding Email.CONTENT_ITEM_TYPE.
                                                                                                     */
                                                                                                    Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";
                                                                                        

                                                                                        Next, define variables to contain the selection argument:

                                                                                            String mSearchString;
                                                                                            String[] mSelectionArgs = { "" };
                                                                                        

                                                                                        Implement onCreateLoader()

                                                                                        Now that you've specified the data you want and how to find it, define a query in your implementation of onCreateLoader(). Return a new CursorLoader from this method, using your projection, selection text expression, and selection array as arguments. For a content URI, use Data.CONTENT_URI. For example:

                                                                                            @Override
                                                                                            public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
                                                                                                // OPTIONAL: Makes search string into pattern
                                                                                                mSearchString = "%" + mSearchString + "%";
                                                                                                // Puts the search string into the selection criteria
                                                                                                mSelectionArgs[0] = mSearchString;
                                                                                                // Starts the query
                                                                                                return new CursorLoader(
                                                                                                        getActivity(),
                                                                                                        Data.CONTENT_URI,
                                                                                                        PROJECTION,
                                                                                                        SELECTION,
                                                                                                        mSelectionArgs,
                                                                                                        null
                                                                                                );
                                                                                            }
                                                                                        

                                                                                        These code snippets are the basis of a simple reverse lookup based on a specific type of detail data. This is the best technique to use if your app focuses on a particular type of data, such as emails, and you want allow users to get the names associated with a piece of data.

                                                                                        Match a Contact By Any Type of Data

                                                                                        Retrieving a contact based on any type of data returns contacts if any of their data matches a the search string, including name, email address, postal address, phone number, and so forth. This results in a broad set of search results. For example, if the search string is "Doe", then searching for any data type returns the contact "John Doe"; it also returns contacts who live on "Doe Street".

                                                                                        To implement this type of retrieval, first implement the following code, as listed in previous sections:

                                                                                        • Request Permission to Read the Provider.
                                                                                        • Define ListView and item layouts.
                                                                                        • Define a Fragment that displays the list of contacts.
                                                                                        • Define global variables.
                                                                                        • Initialize the Fragment.
                                                                                        • Set up the CursorAdapter for the ListView.
                                                                                        • Set the selected contact listener.
                                                                                        • Define a projection.
                                                                                        • Define constants for the Cursor column indexes.

                                                                                          For this type of retrieval, you're using the same table you used in the section Match a Contact by Name and List the Results. Use the same column indexes as well.

                                                                                        • Define the onItemClick() method.
                                                                                        • Initialize the loader.
                                                                                        • Implement onLoadFinished() and onLoaderReset().

                                                                                        The following steps show you the additional code you need to match a search string to any type of data and display the results.

                                                                                        Remove selection criteria

                                                                                        Don't define the SELECTION constants or the mSelectionArgs variable. These aren't used in this type of retrieval.

                                                                                        Implement onCreateLoader()

                                                                                        Implement the onCreateLoader() method, returning a new CursorLoader. You don't need to convert the search string into a pattern, because the Contacts Provider does that automatically. Use Contacts.CONTENT_FILTER_URI as the base URI, and append your search string to it by calling Uri.withAppendedPath(). Using this URI automatically triggers searching for any data type, as shown in the following example:

                                                                                            @Override
                                                                                            public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
                                                                                                /*
                                                                                                 * Appends the search string to the base URI. Always
                                                                                                 * encode search strings to ensure they're in proper
                                                                                                 * format.
                                                                                                 */
                                                                                                Uri contentUri = Uri.withAppendedPath(
                                                                                                        Contacts.CONTENT_FILTER_URI,
                                                                                                        Uri.encode(mSearchString));
                                                                                                // Starts the query
                                                                                                return new CursorLoader(
                                                                                                        getActivity(),
                                                                                                        contentUri,
                                                                                                        PROJECTION,
                                                                                                        null,
                                                                                                        null,
                                                                                                        null
                                                                                                );
                                                                                            }
                                                                                        

                                                                                        These code snippets are the basis of an app that does a broad search of the Contacts Provider. The technique is useful for apps that want to implement functionality similar to the People app's contact list screen.

                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                        This class requires API level or higher

                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                        Retrieving Details for a Contact | Android Developers Skip to content

                                                                                        Most visited

                                                                                        Recently visited

                                                                                        navigation

                                                                                          Retrieving Details for a Contact

                                                                                          This lesson shows how to retrieve detail data for a contact, such as email addresses, phone numbers, and so forth. It's the details that users are looking for when they retrieve a contact. You can give them all the details for a contact, or only display details of a particular type, such as email addresses.

                                                                                          The steps in this lesson assume that you already have a ContactsContract.Contacts row for a contact the user has picked. The Retrieving Contact Names lesson shows how to retrieve a list of contacts.

                                                                                          Retrieve All Details for a Contact

                                                                                          To retrieve all the details for a contact, search the ContactsContract.Data table for any rows that contain the contact's LOOKUP_KEY. This column is available in the ContactsContract.Data table, because the Contacts Provider makes an implicit join between the ContactsContract.Contacts table and the ContactsContract.Data table. The LOOKUP_KEY column is described in more detail in the Retrieving Contact Names lesson.

                                                                                          Note: Retrieving all the details for a contact reduces the performance of a device, because it needs to retrieve all of the columns in the ContactsContract.Data table. Consider the performance impact before you use this technique.

                                                                                          Request permissions

                                                                                          To read from the Contacts Provider, your app must have READ_CONTACTS permission. To request this permission, add the following child element of <manifest> to your manifest file:

                                                                                              <uses-permission android:name="android.permission.READ_CONTACTS" />
                                                                                          

                                                                                          Set up a projection

                                                                                          Depending on the data type a row contains, it may use only a few columns or many. In addition, the data is in different columns depending on the data type. To ensure you get all the possible columns for all possible data types, you need to add all the column names to your projection. Always retrieve Data._ID if you're binding the result Cursor to a ListView; otherwise, the binding won't work. Also retrieve Data.MIMETYPE so you can identify the data type of each row you retrieve. For example:

                                                                                              private static final String PROJECTION =
                                                                                                      {
                                                                                                          Data._ID,
                                                                                                          Data.MIMETYPE,
                                                                                                          Data.DATA1,
                                                                                                          Data.DATA2,
                                                                                                          Data.DATA3,
                                                                                                          Data.DATA4,
                                                                                                          Data.DATA5,
                                                                                                          Data.DATA6,
                                                                                                          Data.DATA7,
                                                                                                          Data.DATA8,
                                                                                                          Data.DATA9,
                                                                                                          Data.DATA10,
                                                                                                          Data.DATA11,
                                                                                                          Data.DATA12,
                                                                                                          Data.DATA13,
                                                                                                          Data.DATA14,
                                                                                                          Data.DATA15
                                                                                                      };
                                                                                          

                                                                                          This projection retrieves all the columns for a row in the ContactsContract.Data table, using the column names defined in the ContactsContract.Data class.

                                                                                          Optionally, you can also use any other column constants defined in or inherited by the ContactsContract.Data class. Notice, however, that the columns SYNC1 through SYNC4 are meant to be used by sync adapters, so their data is not useful.

                                                                                          Define the selection criteria

                                                                                          Define a constant for your selection clause, an array to hold selection arguments, and a variable to hold the selection value. Use the Contacts.LOOKUP_KEY column to find the contact. For example:

                                                                                              // Defines the selection clause
                                                                                              private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
                                                                                              // Defines the array to hold the search criteria
                                                                                              private String[] mSelectionArgs = { "" };
                                                                                              /*
                                                                                               * Defines a variable to contain the selection value. Once you
                                                                                               * have the Cursor from the Contacts table, and you've selected
                                                                                               * the desired row, move the row's LOOKUP_KEY value into this
                                                                                               * variable.
                                                                                               */
                                                                                              private String mLookupKey;
                                                                                          

                                                                                          Using "?" as a placeholder in your selection text expression ensures that the resulting search is generated by binding rather than SQL compilation. This approach eliminates the possibility of malicious SQL injection.

                                                                                          Define the sort order

                                                                                          Define the sort order you want in the resulting Cursor. To keep all rows for a particular data type together, sort by Data.MIMETYPE. This query argument groups all email rows together, all phone rows together, and so forth. For example:

                                                                                              /*
                                                                                               * Defines a string that specifies a sort order of MIME type
                                                                                               */
                                                                                              private static final String SORT_ORDER = Data.MIMETYPE;
                                                                                          

                                                                                          Note: Some data types don't use a subtype, so you can't sort on subtype. Instead, you have to iterate through the returned Cursor, determine the data type of the current row, and store data for rows that use a subtype. When you finish reading the cursor, you can then sort each data type by subtype and display the results.

                                                                                          Initialize the Loader

                                                                                          Always do retrievals from the Contacts Provider (and all other content providers) in a background thread. Use the Loader framework defined by the LoaderManager class and the LoaderManager.LoaderCallbacks interface to do background retrievals.

                                                                                          When you're ready to retrieve the rows, initialize the loader framework by calling initLoader(). Pass an integer identifier to the method; this identifier is passed to LoaderManager.LoaderCallbacks methods. The identifier helps you use multiple loaders in an app by allowing you to differentiate between them.

                                                                                          The following snippet shows how to initialize the loader framework:

                                                                                          public class DetailsFragment extends Fragment implements
                                                                                                  LoaderManager.LoaderCallbacks<Cursor> {
                                                                                              ...
                                                                                              // Defines a constant that identifies the loader
                                                                                              DETAILS_QUERY_ID = 0;
                                                                                              ...
                                                                                              /*
                                                                                               * Invoked when the parent Activity is instantiated
                                                                                               * and the Fragment's UI is ready. Put final initialization
                                                                                               * steps here.
                                                                                               */
                                                                                              @Override
                                                                                              onActivityCreated(Bundle savedInstanceState) {
                                                                                                  ...
                                                                                                  // Initializes the loader framework
                                                                                                  getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
                                                                                          

                                                                                          Implement onCreateLoader()

                                                                                          Implement the onCreateLoader() method, which is called by the loader framework immediately after you call initLoader(). Return a CursorLoader from this method. Since you're searching the ContactsContract.Data table, use the constant Data.CONTENT_URI as the content URI. For example:

                                                                                              @Override
                                                                                              public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
                                                                                                  // Choose the proper action
                                                                                                  switch (loaderId) {
                                                                                                      case DETAILS_QUERY_ID:
                                                                                                      // Assigns the selection parameter
                                                                                                      mSelectionArgs[0] = mLookupKey;
                                                                                                      // Starts the query
                                                                                                      CursorLoader mLoader =
                                                                                                              new CursorLoader(
                                                                                                                      getActivity(),
                                                                                                                      Data.CONTENT_URI,
                                                                                                                      PROJECTION,
                                                                                                                      SELECTION,
                                                                                                                      mSelectionArgs,
                                                                                                                      SORT_ORDER
                                                                                                              );
                                                                                                      ...
                                                                                              }
                                                                                          

                                                                                          Implement onLoadFinished() and onLoaderReset()

                                                                                          Implement the onLoadFinished() method. The loader framework calls onLoadFinished() when the Contacts Provider returns the results of the query. For example:

                                                                                              public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
                                                                                                  switch (loader.getId()) {
                                                                                                      case DETAILS_QUERY_ID:
                                                                                                              /*
                                                                                                               * Process the resulting Cursor here.
                                                                                                               */
                                                                                                          }
                                                                                                          break;
                                                                                                      ...
                                                                                                  }
                                                                                              }
                                                                                          

                                                                                          The method onLoaderReset() is invoked when the loader framework detects that the data backing the result Cursor has changed. At this point, remove any existing references to the Cursor by setting them to null. If you don't, the loader framework won't destroy the old Cursor, and you'll get a memory leak. For example:

                                                                                              @Override
                                                                                              public void onLoaderReset(Loader<Cursor> loader) {
                                                                                                  switch (loader.getId()) {
                                                                                                      case DETAILS_QUERY_ID:
                                                                                                          /*
                                                                                                           * If you have current references to the Cursor,
                                                                                                           * remove them here.
                                                                                                           */
                                                                                                          }
                                                                                                          break;
                                                                                              }
                                                                                          

                                                                                          Retrieve Specific Details for a Contact

                                                                                          Retrieving a specific data type for a contact, such as all the emails, follows the same pattern as retrieving all details. These are the only changes you need to make to the code listed in Retrieve All Details for a Contact:

                                                                                          Projection
                                                                                          Modify your projection to retrieve the columns that are specific to the data type. Also modify the projection to use the column name constants defined in the ContactsContract.CommonDataKinds subclass corresponding to the data type.
                                                                                          Selection
                                                                                          Modify the selection text to search for the MIMETYPE value that's specific to your data type.
                                                                                          Sort order
                                                                                          Since you're only selecting a single detail type, don't group the returned Cursor by Data.MIMETYPE.

                                                                                          These modifications are described in the following sections.

                                                                                          Define a projection

                                                                                          Define the columns you want to retrieve, using the column name constants in the subclass of ContactsContract.CommonDataKinds for the data type. If you plan to bind your Cursor to a ListView, be sure to retrieve the _ID column. For example, to retrieve email data, define the following projection:

                                                                                              private static final String[] PROJECTION =
                                                                                                      {
                                                                                                          Email._ID,
                                                                                                          Email.ADDRESS,
                                                                                                          Email.TYPE,
                                                                                                          Email.LABEL
                                                                                                      };
                                                                                          

                                                                                          Notice that this projection uses the column names defined in the class ContactsContract.CommonDataKinds.Email, instead of the column names defined in the class ContactsContract.Data. Using the email-specific column names makes the code more readable.

                                                                                          In the projection, you can also use any of the other columns defined in the ContactsContract.CommonDataKinds subclass.

                                                                                          Define selection criteria

                                                                                          Define a search text expression that retrieves rows for a specific contact's LOOKUP_KEY and the Data.MIMETYPE of the details you want. Enclose the MIMETYPE value in single quotes by concatenating a "'" (single-quote) character to the start and end of the constant; otherwise, the provider interprets the constant as a variable name rather than as a string value. You don't need to use a placeholder for this value, because you're using a constant rather than a user-supplied value. For example:

                                                                                              /*
                                                                                               * Defines the selection clause. Search for a lookup key
                                                                                               * and the Email MIME type
                                                                                               */
                                                                                              private static final String SELECTION =
                                                                                                      Data.LOOKUP_KEY + " = ?" +
                                                                                                      " AND " +
                                                                                                      Data.MIMETYPE + " = " +
                                                                                                      "'" + Email.CONTENT_ITEM_TYPE + "'";
                                                                                              // Defines the array to hold the search criteria
                                                                                              private String[] mSelectionArgs = { "" };
                                                                                          

                                                                                          Define a sort order

                                                                                          Define a sort order for the returned Cursor. Since you're retrieving a specific data type, omit the sort on MIMETYPE. Instead, if the type of detail data you're searching includes a subtype, sort on it. For example, for email data you can sort on Email.TYPE:

                                                                                              private static final String SORT_ORDER = Email.TYPE + " ASC ";
                                                                                          
                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                          This class requires API level or higher

                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                          Modifying Contacts Using Intents | Android Developers Skip to content

                                                                                          Most visited

                                                                                          Recently visited

                                                                                          navigation

                                                                                            Modifying Contacts Using Intents

                                                                                            This lesson shows you how to use an Intent to insert a new contact or modify a contact's data. Instead of accessing the Contacts Provider directly, an Intent starts the contacts app, which runs the appropriate Activity. For the modification actions described in this lesson, if you send extended data in the Intent it's entered into the UI of the Activity that is started.

                                                                                            Using an Intent to insert or update a single contact is the preferred way of modifying the Contacts Provider, for the following reasons:

                                                                                            • It saves you the time and and effort of developing your own UI and code.
                                                                                            • It avoids introducing errors caused by modifications that don't follow the Contacts Provider's rules.
                                                                                            • It reduces the number of permissions you need to request. Your app doesn't need permission to write to the Contacts Provider, because it delegates modifications to the contacts app, which already has that permission.

                                                                                            Insert a New Contact Using an Intent

                                                                                            You often want to allow the user to insert a new contact when your app receives new data. For example, a restaurant review app can allow users to add the restaurant as a contact as they're reviewing it. To do this using an intent, create the intent using as much data as you have available, and then send the intent to the contacts app.

                                                                                            Inserting a contact using the contacts app inserts a new raw contact into the Contacts Provider's ContactsContract.RawContacts table. If necessary, the contacts app prompts users for the account type and account to use when creating the raw contact. The contacts app also notifies users if the raw contact already exists. Users then have option of canceling the insertion, in which case no contact is created. To learn more about raw contacts, see the Contacts Provider API guide.

                                                                                            Create an Intent

                                                                                            To start, create a new Intent object with the action Intents.Insert.ACTION. Set the MIME type to RawContacts.CONTENT_TYPE. For example:

                                                                                            ...
                                                                                            // Creates a new Intent to insert a contact
                                                                                            Intent intent = new Intent(Intents.Insert.ACTION);
                                                                                            // Sets the MIME type to match the Contacts Provider
                                                                                            intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
                                                                                            

                                                                                            If you already have details for the contact, such as a phone number or email address, you can insert them into the intent as extended data. For a key value, use the appropriate constant from Intents.Insert. The contacts app displays the data in its insert screen, allowing users to make further edits and additions.

                                                                                            /* Assumes EditText fields in your UI contain an email address
                                                                                             * and a phone number.
                                                                                             *
                                                                                             */
                                                                                            private EditText mEmailAddress = (EditText) findViewById(R.id.email);
                                                                                            private EditText mPhoneNumber = (EditText) findViewById(R.id.phone);
                                                                                            ...
                                                                                            /*
                                                                                             * Inserts new data into the Intent. This data is passed to the
                                                                                             * contacts app's Insert screen
                                                                                             */
                                                                                            // Inserts an email address
                                                                                            intent.putExtra(Intents.Insert.EMAIL, mEmailAddress.getText())
                                                                                            /*
                                                                                             * In this example, sets the email type to be a work email.
                                                                                             * You can set other email types as necessary.
                                                                                             */
                                                                                                  .putExtra(Intents.Insert.EMAIL_TYPE, CommonDataKinds.Email.TYPE_WORK)
                                                                                            // Inserts a phone number
                                                                                                  .putExtra(Intents.Insert.PHONE, mPhoneNumber.getText())
                                                                                            /*
                                                                                             * In this example, sets the phone type to be a work phone.
                                                                                             * You can set other phone types as necessary.
                                                                                             */
                                                                                                  .putExtra(Intents.Insert.PHONE_TYPE, Phone.TYPE_WORK);
                                                                                            
                                                                                            

                                                                                            Once you've created the Intent, send it by calling startActivity().

                                                                                                /* Sends the Intent
                                                                                                 */
                                                                                                startActivity(intent);
                                                                                            

                                                                                            This call opens a screen in the contacts app that allows users to enter a new contact. The account type and account name for the contact is listed at the top of the screen. Once users enter the data and click Done, the contacts app's contact list appears. Users return to your app by clicking Back.

                                                                                            Edit an Existing Contact Using an Intent

                                                                                            Editing an existing contact using an Intent is useful if the user has already chosen a contact of interest. For example, an app that finds contacts that have postal addresses but lack a postal code could give users the option of looking up the code and then adding it to the contact.

                                                                                            To edit an existing contact using an intent, use a procedure similar to inserting a contact. Create an intent as described in the section Insert a New Contact Using an Intent, but add the contact's Contacts.CONTENT_LOOKUP_URI and the MIME type Contacts.CONTENT_ITEM_TYPE to the intent. If you want to edit the contact with details you already have, you can put them in the intent's extended data. Notice that some name columns can't be edited using an intent; these columns are listed in the summary section of the API reference for the class ContactsContract.Contacts under the heading "Update".

                                                                                            Finally, send the intent. In response, the contacts app displays an edit screen. When the user finishes editing and saves the edits, the contacts app displays a contact list. When the user clicks Back, your app is displayed.

                                                                                            Create the Intent

                                                                                            To edit a contact, call Intent(action) to create an intent with the action ACTION_EDIT. Call setDataAndType() to set the data value for the intent to the contact's Contacts.CONTENT_LOOKUP_URI and the MIME type to Contacts.CONTENT_ITEM_TYPE MIME type; because a call to setType() overwrites the current data value for the Intent, you must set the data and the MIME type at the same time.

                                                                                            To get a contact's Contacts.CONTENT_LOOKUP_URI, call Contacts.getLookupUri(id, lookupkey) with the contact's Contacts._ID and Contacts.LOOKUP_KEY values as arguments.

                                                                                            The following snippet shows you how to create an intent:

                                                                                                // The Cursor that contains the Contact row
                                                                                                public Cursor mCursor;
                                                                                                // The index of the lookup key column in the cursor
                                                                                                public int mLookupKeyIndex;
                                                                                                // The index of the contact's _ID value
                                                                                                public int mIdIndex;
                                                                                                // The lookup key from the Cursor
                                                                                                public String mCurrentLookupKey;
                                                                                                // The _ID value from the Cursor
                                                                                                public long mCurrentId;
                                                                                                // A content URI pointing to the contact
                                                                                                Uri mSelectedContactUri;
                                                                                                ...
                                                                                                /*
                                                                                                 * Once the user has selected a contact to edit,
                                                                                                 * this gets the contact's lookup key and _ID values from the
                                                                                                 * cursor and creates the necessary URI.
                                                                                                 */
                                                                                                // Gets the lookup key column index
                                                                                                mLookupKeyIndex = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
                                                                                                // Gets the lookup key value
                                                                                                mCurrentLookupKey = mCursor.getString(mLookupKeyIndex);
                                                                                                // Gets the _ID column index
                                                                                                mIdIndex = mCursor.getColumnIndex(Contacts._ID);
                                                                                                mCurrentId = mCursor.getLong(mIdIndex);
                                                                                                mSelectedContactUri =
                                                                                                        Contacts.getLookupUri(mCurrentId, mCurrentLookupKey);
                                                                                                ...
                                                                                                // Creates a new Intent to edit a contact
                                                                                                Intent editIntent = new Intent(Intent.ACTION_EDIT);
                                                                                                /*
                                                                                                 * Sets the contact URI to edit, and the data type that the
                                                                                                 * Intent must match
                                                                                                 */
                                                                                                editIntent.setDataAndType(mSelectedContactUri,Contacts.CONTENT_ITEM_TYPE);
                                                                                            

                                                                                            Add the navigation flag

                                                                                            In Android 4.0 (API version 14) and later, a problem in the contacts app causes incorrect navigation. When your app sends an edit intent to the contacts app, and users edit and save a contact, when they click Back they see the contacts list screen. To navigate back to your app, they have to click Recents and choose your app.

                                                                                            To work around this problem in Android 4.0.3 (API version 15) and later, add the extended data key finishActivityOnSaveCompleted to the intent, with a value of true. Android versions prior to Android 4.0 accept this key, but it has no effect. To set the extended data, do the following:

                                                                                                // Sets the special extended data for navigation
                                                                                                editIntent.putExtra("finishActivityOnSaveCompleted", true);
                                                                                            

                                                                                            Add other extended data

                                                                                            To add additional extended data to the Intent, call putExtra() as desired. You can add extended data for common contact fields by using the key values specified in Intents.Insert. Remember that some columns in the ContactsContract.Contacts table can't be modified. These columns are listed in the summary section of the API reference for the class ContactsContract.Contacts under the heading "Update".

                                                                                            Send the Intent

                                                                                            Finally, send the intent you've constructed. For example:

                                                                                                // Sends the Intent
                                                                                                startActivity(editIntent);
                                                                                            

                                                                                            Let Users Choose to Insert or Edit Using an Intent

                                                                                            You can allow users to choose whether to insert a contact or edit an existing one by sending an Intent with the action ACTION_INSERT_OR_EDIT. For example, an email client app could allow users to add an incoming email address to a new contact, or add it as an additional address for an existing contact. Set the MIME type for this intent to Contacts.CONTENT_ITEM_TYPE, but don't set the data URI.

                                                                                            When you send this intent, the contacts app displays a list of contacts. Users can either insert a new contact or pick an existing contact and edit it. Any extended data fields you add to the intent populates the screen that appears. You can use any of the key values specified in Intents.Insert. The following code snippet shows how to construct and send the intent:

                                                                                                // Creates a new Intent to insert or edit a contact
                                                                                                Intent intentInsertEdit = new Intent(Intent.ACTION_INSERT_OR_EDIT);
                                                                                                // Sets the MIME type
                                                                                                intentInsertEdit.setType(Contacts.CONTENT_ITEM_TYPE);
                                                                                                // Add code here to insert extended data, if desired
                                                                                                ...
                                                                                                // Sends the Intent with an request ID
                                                                                                startActivity(intentInsertEdit);
                                                                                            
                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                            This class requires API level or higher

                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                            Displaying the Quick Contact Badge | Android Developers Skip to content

                                                                                            Most visited

                                                                                            Recently visited

                                                                                            navigation

                                                                                              Displaying the Quick Contact Badge

                                                                                              This lesson shows you how to add a QuickContactBadge to your UI and how to bind data to it. A QuickContactBadge is a widget that initially appears as a thumbnail image. Although you can use any Bitmap for the thumbnail image, you usually use a Bitmap decoded from the contact's photo thumbnail image.

                                                                                              The small image acts as a control; when users click on the image, the QuickContactBadge expands into a dialog containing the following:

                                                                                              A large image
                                                                                              The large image associated with the contact, or no image is available, a placeholder graphic.
                                                                                              App icons
                                                                                              An app icon for each piece of detail data that can be handled by a built-in app. For example, if the contact's details include one or more email addresses, an email icon appears. When users click the icon, all of the contact's email addresses appear. When users click one of the addresses, the email app displays a screen for composing a message to the selected email address.

                                                                                              The QuickContactBadge view provides instant access to a contact's details, as well as a fast way of communicating with the contact. Users don't have to look up a contact, find and copy information, and then paste it into the appropriate app. Instead, they can click on the QuickContactBadge, choose the communication method they want to use, and send the information for that method directly to the appropriate app.

                                                                                              Add a QuickContactBadge View

                                                                                              To add a QuickContactBadge, insert a <QuickContactBadge> element in your layout. For example:

                                                                                              <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                              android:layout_width="match_parent"
                                                                                                              android:layout_height="match_parent">
                                                                                              ...
                                                                                                  <QuickContactBadge
                                                                                                             android:id=@+id/quickbadge
                                                                                                             android:layout_height="wrap_content"
                                                                                                             android:layout_width="wrap_content"
                                                                                                             android:scaleType="centerCrop"/>
                                                                                                  ...
                                                                                              </RelativeLayout>
                                                                                              

                                                                                              Retrieve provider data

                                                                                              To display a contact in the QuickContactBadge, you need a content URI for the contact and a Bitmap for the small image. You generate both the content URI and the Bitmap from columns retrieved from the Contacts Provider. Specify these columns as part of the projection you use to load data into your Cursor.

                                                                                              For Android 3.0 (API level 11) and later, include the following columns in your projection:

                                                                                              For Android 2.3.3 (API level 10) and earlier, use the following columns:

                                                                                              The remainder of this lesson assumes that you've already loaded a Cursor that contains these columns as well as others you may have chosen. To learn how to retrieve this columns in a Cursor, read the lesson Retrieving a List of Contacts.

                                                                                              Set the Contact URI and Thumbnail

                                                                                              Once you have the necessary columns, you can bind data to the QuickContactBadge.

                                                                                              Set the Contact URI

                                                                                              To set the content URI for the contact, call getLookupUri(id,lookupKey) to get a CONTENT_LOOKUP_URI, then call assignContactUri() to set the contact. For example:

                                                                                                  // The Cursor that contains contact rows
                                                                                                  Cursor mCursor;
                                                                                                  // The index of the _ID column in the Cursor
                                                                                                  int mIdColumn;
                                                                                                  // The index of the LOOKUP_KEY column in the Cursor
                                                                                                  int mLookupKeyColumn;
                                                                                                  // A content URI for the desired contact
                                                                                                  Uri mContactUri;
                                                                                                  // A handle to the QuickContactBadge view
                                                                                                  QuickContactBadge mBadge;
                                                                                                  ...
                                                                                                  mBadge = (QuickContactBadge) findViewById(R.id.quickbadge);
                                                                                                  /*
                                                                                                   * Insert code here to move to the desired cursor row
                                                                                                   */
                                                                                                  // Gets the _ID column index
                                                                                                  mIdColumn = mCursor.getColumnIndex(Contacts._ID);
                                                                                                  // Gets the LOOKUP_KEY index
                                                                                                  mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
                                                                                                  // Gets a content URI for the contact
                                                                                                  mContactUri =
                                                                                                          Contacts.getLookupUri(
                                                                                                              mCursor.getLong(mIdColumn),
                                                                                                              mCursor.getString(mLookupKeyColumn)
                                                                                                          );
                                                                                                  mBadge.assignContactUri(mContactUri);
                                                                                              

                                                                                              When users click the QuickContactBadge icon, the contact's details automatically appear in the dialog.

                                                                                              Set the photo thumbnail

                                                                                              Setting the contact URI for the QuickContactBadge does not automatically load the contact's thumbnail photo. To load the photo, get a URI for the photo from the contact's Cursor row, use it to open the file containing the compressed thumbnail photo, and read the file into a Bitmap.

                                                                                              Note: The PHOTO_THUMBNAIL_URI column isn't available in platform versions prior to 3.0. For those versions, you must retrieve the URI from the Contacts.Photo subtable.

                                                                                              First, set up variables for accessing the Cursor containing the Contacts._ID and Contacts.LOOKUP_KEY columns, as described previously:

                                                                                                  // The column in which to find the thumbnail ID
                                                                                                  int mThumbnailColumn;
                                                                                                  /*
                                                                                                   * The thumbnail URI, expressed as a String.
                                                                                                   * Contacts Provider stores URIs as String values.
                                                                                                   */
                                                                                                  String mThumbnailUri;
                                                                                                  ...
                                                                                                  /*
                                                                                                   * Gets the photo thumbnail column index if
                                                                                                   * platform version >= Honeycomb
                                                                                                   */
                                                                                                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                                                                                                      mThumbnailColumn =
                                                                                                              mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
                                                                                                  // Otherwise, sets the thumbnail column to the _ID column
                                                                                                  } else {
                                                                                                      mThumbnailColumn = mIdColumn;
                                                                                                  }
                                                                                                  /*
                                                                                                   * Assuming the current Cursor position is the contact you want,
                                                                                                   * gets the thumbnail ID
                                                                                                   */
                                                                                                  mThumbnailUri = mCursor.getString(mThumbnailColumn);
                                                                                                  ...
                                                                                              

                                                                                              Define a method that takes photo-related data for the contact and dimensions for the destination view, and returns the properly-sized thumbnail in a Bitmap. Start by constructing a URI that points to the thumbnail:

                                                                                                  /**
                                                                                                   * Load a contact photo thumbnail and return it as a Bitmap,
                                                                                                   * resizing the image to the provided image dimensions as needed.
                                                                                                   * @param photoData photo ID Prior to Honeycomb, the contact's _ID value.
                                                                                                   * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI.
                                                                                                   * @return A thumbnail Bitmap, sized to the provided width and height.
                                                                                                   * Returns null if the thumbnail is not found.
                                                                                                   */
                                                                                                  private Bitmap loadContactPhotoThumbnail(String photoData) {
                                                                                                      // Creates an asset file descriptor for the thumbnail file.
                                                                                                      AssetFileDescriptor afd = null;
                                                                                                      // try-catch block for file not found
                                                                                                      try {
                                                                                                          // Creates a holder for the URI.
                                                                                                          Uri thumbUri;
                                                                                                          // If Android 3.0 or later
                                                                                                          if (Build.VERSION.SDK_INT
                                                                                                                  >=
                                                                                                              Build.VERSION_CODES.HONEYCOMB) {
                                                                                                              // Sets the URI from the incoming PHOTO_THUMBNAIL_URI
                                                                                                              thumbUri = Uri.parse(photoData);
                                                                                                          } else {
                                                                                                          // Prior to Android 3.0, constructs a photo Uri using _ID
                                                                                                              /*
                                                                                                               * Creates a contact URI from the Contacts content URI
                                                                                                               * incoming photoData (_ID)
                                                                                                               */
                                                                                                              final Uri contactUri = Uri.withAppendedPath(
                                                                                                                      Contacts.CONTENT_URI, photoData);
                                                                                                              /*
                                                                                                               * Creates a photo URI by appending the content URI of
                                                                                                               * Contacts.Photo.
                                                                                                               */
                                                                                                              thumbUri =
                                                                                                                      Uri.withAppendedPath(
                                                                                                                              contactUri, Photo.CONTENT_DIRECTORY);
                                                                                                          }
                                                                                                  
                                                                                                      /*
                                                                                                       * Retrieves an AssetFileDescriptor object for the thumbnail
                                                                                                       * URI
                                                                                                       * using ContentResolver.openAssetFileDescriptor
                                                                                                       */
                                                                                                      afd = getActivity().getContentResolver().
                                                                                                              openAssetFileDescriptor(thumbUri, "r");
                                                                                                      /*
                                                                                                       * Gets a file descriptor from the asset file descriptor.
                                                                                                       * This object can be used across processes.
                                                                                                       */
                                                                                                      FileDescriptor fileDescriptor = afd.getFileDescriptor();
                                                                                                      // Decode the photo file and return the result as a Bitmap
                                                                                                      // If the file descriptor is valid
                                                                                                      if (fileDescriptor != null) {
                                                                                                          // Decodes the bitmap
                                                                                                          return BitmapFactory.decodeFileDescriptor(
                                                                                                                  fileDescriptor, null, null);
                                                                                                          }
                                                                                                      // If the file isn't found
                                                                                                      } catch (FileNotFoundException e) {
                                                                                                          /*
                                                                                                           * Handle file not found errors
                                                                                                           */
                                                                                                      // In all cases, close the asset file descriptor
                                                                                                      } finally {
                                                                                                          if (afd != null) {
                                                                                                              try {
                                                                                                                  afd.close();
                                                                                                              } catch (IOException e) {}
                                                                                                          }
                                                                                                      }
                                                                                                      return null;
                                                                                                  }
                                                                                              

                                                                                              Call the loadContactPhotoThumbnail() method in your code to get the thumbnail Bitmap, and use the result to set the photo thumbnail in your QuickContactBadge:

                                                                                                  ...
                                                                                                  /*
                                                                                                   * Decodes the thumbnail file to a Bitmap.
                                                                                                   */
                                                                                                  Bitmap mThumbnail =
                                                                                                          loadContactPhotoThumbnail(mThumbnailUri);
                                                                                                  /*
                                                                                                   * Sets the image in the QuickContactBadge
                                                                                                   * QuickContactBadge inherits from ImageView, so
                                                                                                   */
                                                                                                  mBadge.setImageBitmap(mThumbnail);
                                                                                              

                                                                                              Add a QuickContactBadge to a ListView

                                                                                              A QuickContactBadge is a useful addition to a ListView that displays a list of contacts. Use the QuickContactBadge to display a thumbnail photo for each contact; when users click the thumbnail, the QuickContactBadge dialog appears.

                                                                                              Add the QuickContactBadge element

                                                                                              To start, add a QuickContactBadge view element to your item layout For example, if you want to display a QuickContactBadge and a name for each contact you retrieve, put the following XML into a layout file:

                                                                                              <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                              android:layout_width="match_parent"
                                                                                                              android:layout_height="wrap_content">
                                                                                                  <QuickContactBadge
                                                                                                      android:id="@+id/quickcontact"
                                                                                                      android:layout_height="wrap_content"
                                                                                                      android:layout_width="wrap_content"
                                                                                                      android:scaleType="centerCrop"/>
                                                                                                  <TextView android:id="@+id/displayname"
                                                                                                            android:layout_width="match_parent"
                                                                                                            android:layout_height="wrap_content"
                                                                                                            android:layout_toRightOf="@+id/quickcontact"
                                                                                                            android:gravity="center_vertical"
                                                                                                            android:layout_alignParentRight="true"
                                                                                                            android:layout_alignParentTop="true"/>
                                                                                              </RelativeLayout>
                                                                                              

                                                                                              In the following sections, this file is referred to as contact_item_layout.xml.

                                                                                              Set up a custom CursorAdapter

                                                                                              To bind a CursorAdapter to a ListView containing a QuickContactBadge, define a custom adapter that extends CursorAdapter. This approach allows you to process the data in the Cursor before you bind it to the QuickContactBadge. This approach also allows you to bind multiple Cursor columns to the QuickContactBadge. Neither of these operations is possible in a regular CursorAdapter.

                                                                                              The subclass of CursorAdapter that you define must override the following methods:

                                                                                              CursorAdapter.newView()
                                                                                              Inflates a new View object to hold the item layout. In the override of this method, store handles to the child View objects of the layout, including the child QuickContactBadge. By taking this approach, you avoid having to get handles to the child View objects each time you inflate a new layout.

                                                                                              You must override this method so you can get handles to the individual child View objects. This technique allows you to control their binding in CursorAdapter.bindView().

                                                                                              CursorAdapter.bindView()
                                                                                              Moves data from the current Cursor row to the child View objects of the item layout. You must override this method so you can bind both the contact's URI and thumbnail to the QuickContactBadge. The default implementation only allows a 1-to-1 mapping between a column and a View

                                                                                              The following code snippet contains an example of a custom subclass of CursorAdapter:

                                                                                              Define the custom list adapter

                                                                                              Define the subclass of CursorAdapter including its constructor, and override newView() and bindView():

                                                                                                  /**
                                                                                                   *
                                                                                                   *
                                                                                                   */
                                                                                                  private class ContactsAdapter extends CursorAdapter {
                                                                                                      private LayoutInflater mInflater;
                                                                                                      ...
                                                                                                      public ContactsAdapter(Context context) {
                                                                                                          super(context, null, 0);
                                                                                              
                                                                                                          /*
                                                                                                           * Gets an inflater that can instantiate
                                                                                                           * the ListView layout from the file.
                                                                                                           */
                                                                                                          mInflater = LayoutInflater.from(context);
                                                                                                          ...
                                                                                                      }
                                                                                                      ...
                                                                                                      /**
                                                                                                       * Defines a class that hold resource IDs of each item layout
                                                                                                       * row to prevent having to look them up each time data is
                                                                                                       * bound to a row.
                                                                                                       */
                                                                                                      private class ViewHolder {
                                                                                                          TextView displayname;
                                                                                                          QuickContactBadge quickcontact;
                                                                                                      }
                                                                                                      ..
                                                                                                      @Override
                                                                                                      public View newView(
                                                                                                              Context context,
                                                                                                              Cursor cursor,
                                                                                                              ViewGroup viewGroup) {
                                                                                                          /* Inflates the item layout. Stores resource IDs in a
                                                                                                           * in a ViewHolder class to prevent having to look
                                                                                                           * them up each time bindView() is called.
                                                                                                           */
                                                                                                          final View itemView =
                                                                                                                  mInflater.inflate(
                                                                                                                          R.layout.contact_list_layout,
                                                                                                                          viewGroup,
                                                                                                                          false
                                                                                                                  );
                                                                                                          final ViewHolder holder = new ViewHolder();
                                                                                                          holder.displayname =
                                                                                                                  (TextView) view.findViewById(R.id.displayname);
                                                                                                          holder.quickcontact =
                                                                                                                  (QuickContactBadge)
                                                                                                                          view.findViewById(R.id.quickcontact);
                                                                                                          view.setTag(holder);
                                                                                                          return view;
                                                                                                      }
                                                                                                      ...
                                                                                                      @Override
                                                                                                      public void bindView(
                                                                                                              View view,
                                                                                                              Context context,
                                                                                                              Cursor cursor) {
                                                                                                          final ViewHolder holder = (ViewHolder) view.getTag();
                                                                                                          final String photoData =
                                                                                                                  cursor.getString(mPhotoDataIndex);
                                                                                                          final String displayName =
                                                                                                                  cursor.getString(mDisplayNameIndex);
                                                                                                          ...
                                                                                                          // Sets the display name in the layout
                                                                                                          holder.displayname = cursor.getString(mDisplayNameIndex);
                                                                                                          ...
                                                                                                          /*
                                                                                                           * Generates a contact URI for the QuickContactBadge.
                                                                                                           */
                                                                                                          final Uri contactUri = Contacts.getLookupUri(
                                                                                                                  cursor.getLong(mIdIndex),
                                                                                                                  cursor.getString(mLookupKeyIndex));
                                                                                                          holder.quickcontact.assignContactUri(contactUri);
                                                                                                          String photoData = cursor.getString(mPhotoDataIndex);
                                                                                                          /*
                                                                                                           * Decodes the thumbnail file to a Bitmap.
                                                                                                           * The method loadContactPhotoThumbnail() is defined
                                                                                                           * in the section "Set the Contact URI and Thumbnail"
                                                                                                           */
                                                                                                          Bitmap thumbnailBitmap =
                                                                                                                  loadContactPhotoThumbnail(photoData);
                                                                                                          /*
                                                                                                           * Sets the image in the QuickContactBadge
                                                                                                           * QuickContactBadge inherits from ImageView
                                                                                                           */
                                                                                                          holder.quickcontact.setImageBitmap(thumbnailBitmap);
                                                                                                  }
                                                                                              

                                                                                              Set up variables

                                                                                              In your code, set up variables, including a Cursor projection that includes the necessary columns.

                                                                                              Note: The following code snippets use the method loadContactPhotoThumbnail(), which is defined in the section Set the Contact URI and Thumbnail

                                                                                              For example:

                                                                                              public class ContactsFragment extends Fragment implements
                                                                                                      LoaderManager.LoaderCallbacks<Cursor> {
                                                                                              ...
                                                                                                  // Defines a ListView
                                                                                                  private ListView mListView;
                                                                                                  // Defines a ContactsAdapter
                                                                                                  private ContactsAdapter mAdapter;
                                                                                                  ...
                                                                                                  // Defines a Cursor to contain the retrieved data
                                                                                                  private Cursor mCursor;
                                                                                                  /*
                                                                                                   * Defines a projection based on platform version. This ensures
                                                                                                   * that you retrieve the correct columns.
                                                                                                   */
                                                                                                  private static final String[] PROJECTION =
                                                                                                          {
                                                                                                              Contacts._ID,
                                                                                                              Contacts.LOOKUP_KEY,
                                                                                                              (Build.VERSION.SDK_INT >=
                                                                                                               Build.VERSION_CODES.HONEYCOMB) ?
                                                                                                                      Contacts.DISPLAY_NAME_PRIMARY :
                                                                                                                      Contacts.DISPLAY_NAME
                                                                                                              (Build.VERSION.SDK_INT >=
                                                                                                               Build.VERSION_CODES.HONEYCOMB) ?
                                                                                                                      Contacts.PHOTO_THUMBNAIL_ID :
                                                                                                                      /*
                                                                                                                       * Although it's not necessary to include the
                                                                                                                       * column twice, this keeps the number of
                                                                                                                       * columns the same regardless of version
                                                                                                                       */
                                                                                                                      Contacts_ID
                                                                                                              ...
                                                                                                          };
                                                                                                  /*
                                                                                                   * As a shortcut, defines constants for the
                                                                                                   * column indexes in the Cursor. The index is
                                                                                                   * 0-based and always matches the column order
                                                                                                   * in the projection.
                                                                                                   */
                                                                                                  // Column index of the _ID column
                                                                                                  private int mIdIndex = 0;
                                                                                                  // Column index of the LOOKUP_KEY column
                                                                                                  private int mLookupKeyIndex = 1;
                                                                                                  // Column index of the display name column
                                                                                                  private int mDisplayNameIndex = 3;
                                                                                                  /*
                                                                                                   * Column index of the photo data column.
                                                                                                   * It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
                                                                                                   * and _ID for previous versions.
                                                                                                   */
                                                                                                  private int mPhotoDataIndex =
                                                                                                          Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                                                                                                          3 :
                                                                                                          0;
                                                                                                  ...
                                                                                              

                                                                                              Set up the ListView

                                                                                              In Fragment.onCreate(), instantiate the custom cursor adapter and get a handle to the ListView:

                                                                                                  @Override
                                                                                                  public void onCreate(Bundle savedInstanceState) {
                                                                                                      ...
                                                                                                      /*
                                                                                                       * Instantiates the subclass of
                                                                                                       * CursorAdapter
                                                                                                       */
                                                                                                      ContactsAdapter mContactsAdapter =
                                                                                                              new ContactsAdapter(getActivity());
                                                                                                      /*
                                                                                                       * Gets a handle to the ListView in the file
                                                                                                       * contact_list_layout.xml
                                                                                                       */
                                                                                                      mListView = (ListView) findViewById(R.layout.contact_list_layout);
                                                                                                      ...
                                                                                                  }
                                                                                                  ...
                                                                                              

                                                                                              In onActivityCreated(), bind the ContactsAdapter to the ListView:

                                                                                                  @Override
                                                                                                  public void onActivityCreated(Bundle savedInstanceState) {
                                                                                                      ...
                                                                                                      // Sets up the adapter for the ListView
                                                                                                      mListView.setAdapter(mAdapter);
                                                                                                      ...
                                                                                                  }
                                                                                                  ...
                                                                                              

                                                                                              When you get back a Cursor containing the contacts data, usually in onLoadFinished(), call swapCursor() to move the Cursor data to the ListView. This displays the QuickContactBadge for each entry in the list of contacts:

                                                                                                  public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
                                                                                                      // When the loader has completed, swap the cursor into the adapter.
                                                                                                      mContactsAdapter.swapCursor(cursor);
                                                                                                  }
                                                                                              

                                                                                              When you bind a Cursor to a ListView with a CursorAdapter (or subclass), and you use a CursorLoader to load the Cursor, always clear references to the Cursor in your implementation of onLoaderReset(). For example:

                                                                                                  @Override
                                                                                                  public void onLoaderReset(Loader<Cursor> loader) {
                                                                                                      // Removes remaining reference to the previous Cursor
                                                                                                      mContactsAdapter.swapCursor(null);
                                                                                                  }
                                                                                              
                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                              This class requires API level or higher

                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                              Building Apps for Wearables | Android Developers Skip to content

                                                                                              Most visited

                                                                                              Recently visited

                                                                                              navigation

                                                                                                Building Apps for Wearables

                                                                                                These classes teach you how to build notifications in a handheld app that are automatically synced to wearables as well as how to build apps that run on wearables.

                                                                                                Note: For more information about the APIs used in these training classes, see the Wear API reference documentation.

                                                                                                If you prefer to learn through interactive video training, check out this online course about Android Wear Development.

                                                                                                Start the video course

                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                This class requires API level or higher

                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                Adding Wearable Features to Notifications | Android Developers Skip to content

                                                                                                Most visited

                                                                                                Recently visited

                                                                                                navigation

                                                                                                  Adding Wearable Features to Notifications

                                                                                                  Dependencies and prerequisites

                                                                                                  You should also read

                                                                                                  When an Android handheld (phone or tablet) and Android wearable are connected, the handheld automatically shares notifications with the wearable. On the wearable, each notification appears as a new card in the context stream.

                                                                                                  However, to give users the best experience, you should add wearable-specific functionality to the notifications you already create. The following lessons show you how to create notifications that are catered for handhelds and wearables at the same time.

                                                                                                  Figure 1. The same notification displayed on a handheld and wearable.

                                                                                                  Lessons

                                                                                                  Creating a notification
                                                                                                  Learn how to create notifications with wearable features with the Android support library.
                                                                                                  Receiving Voice Input in a Notification
                                                                                                  Learn how to add an action to a wearable notification that receives voice input from users and delivers the transcribed message to your handset app.
                                                                                                  Adding Pages to a Notification
                                                                                                  Learn how to add additional pages of information that are visible when the user swipes to the left.
                                                                                                  Stacking Notifications
                                                                                                  Learn how to place all similar notifications from your app in a stack, allowing users to view each notification individually without adding multiple cards to the card stream.
                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                  This class requires API level or higher

                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                  Creating Wearable Apps | Android Developers Skip to content

                                                                                                  Most visited

                                                                                                  Recently visited

                                                                                                  navigation

                                                                                                    Creating Wearable Apps

                                                                                                    Dependencies and Prerequisites

                                                                                                    • An Android Wear device

                                                                                                    Wearable apps run directly on the device, giving you access to hardware such as sensors and the GPU. They are fundamentally the same as apps built for other devices using the Android SDK, but differ greatly in design and usability and the amount of functionality provided. These are the main differences between handheld and wearable apps:

                                                                                                    • Wearable apps are relatively small in size and functionality compared to handheld apps. They contain only what makes sense on the wearable, which is usually a small subset of the corresponding handheld app. In general, you should carry out operations on the handheld when possible and send the results to the wearable.
                                                                                                    • Users don't download apps directly onto the wearable. Instead, you bundle the wearable app inside the handheld app. When users install the handheld app, the system automatically installs the wearable app. However, for development purposes, you can still install the wearable app directly to the wearable.
                                                                                                    • Wearable apps can access much of the standard Android APIs, but don't support the following APIs:

                                                                                                      You can check if a wearable supports a feature by calling hasSystemFeature() before trying to use an API.

                                                                                                    To conserve power on a wearable device, you can enable ambient mode for your Wear app. Devices transition from interactive to ambient mode when the user is idle on an activity or when the user covers the screen with their palm. Wearable apps that can transition into ambient mode are called always-on apps. The following describes the two modes of operation for always-on apps:

                                                                                                    Interactive
                                                                                                    Use full color with fluid animation in this mode. The app is also responsive to input.
                                                                                                    Ambient
                                                                                                    Render the screen with grayscale graphics and do not present any input cues in this mode. This display mode is only supported on devices running Android 5.1 or higher.

                                                                                                    On devices running versions prior to Android 5.1 or for apps that do not support ambient mode, when a user is idle on an activity or when the user covers the screen with their palm on an activity, the Wear home screen is displayed instead of your activity in ambient mode. If you need to show persistent content on versions prior to Android 5.1, create a notification in the context stream instead.

                                                                                                    Note: We recommend using Android Studio for Android Wear development, as it provides project setup, library inclusion, and packaging conveniences. The rest of this training assumes you're using Android Studio.

                                                                                                    Lessons

                                                                                                    Creating and Running a Wearable App
                                                                                                    Learn how to create an Android Studio project that contains both the wearable and handheld app modules and how to run the app on a device or emulator.
                                                                                                    Creating Custom Layouts
                                                                                                    Learn how to create and display custom layouts for notifications and activities.
                                                                                                    Keeping Your App Visible
                                                                                                    Learn how to enable ambient mode for your apps, so they remain visible while still saving battery power.
                                                                                                    Adding Voice Capabilities
                                                                                                    Learn how to launch an activity with voice actions and how to start the system speech recognizer app to obtain free-form voice input.
                                                                                                    Packaging Wearable Apps
                                                                                                    Learn how to package a wearable app inside a handheld app. This allows the system to install the wearable app automatically when users install the companion handheld app from the Google Play store.
                                                                                                    Debugging over Bluetooth
                                                                                                    Learn how to debug your wearable over Bluetooth instead of USB.
                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                    This class requires API level or higher

                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                    Creating Custom UIs for Wear Devices | Android Developers Skip to content

                                                                                                    Most visited

                                                                                                    Recently visited

                                                                                                    navigation

                                                                                                      Creating Custom UIs for Wear Devices

                                                                                                      Dependencies and Prerequisites

                                                                                                      • Android 4.4W (API level 20) or higher on the wearable device

                                                                                                      User interfaces for wearable apps differ significantly from those built for handheld devices. Apps for wearables should follow the Android Wear design principles and implement the recommended UI patterns, which ensure a consistent user experience across apps that is optimized for wearables.

                                                                                                      This class teaches you how to create custom UIs for your wearable apps and custom notifications that look good on any Android Wear device by implementing these UI patterns:

                                                                                                      • Cards
                                                                                                      • Countdowns and confirmations
                                                                                                      • Long press to dismiss
                                                                                                      • 2D Pickers
                                                                                                      • Selection lists

                                                                                                      The Wearable UI Library, which is part of the Google Repository in the Android SDK, provides classes that help you implement these patterns and create layouts that work on both round and square Android Wear devices.

                                                                                                      Note: We recommend using Android Studio for Android Wear development, as it provides project setup, library inclusion, and packaging conveniences. This training assumes you are using Android Studio.

                                                                                                      Lessons

                                                                                                      Defining Layouts
                                                                                                      Learn how to create layouts that look good on round and square Android Wear devices.
                                                                                                      Creating Cards
                                                                                                      Learn how to create cards with custom layouts.
                                                                                                      Creating Lists
                                                                                                      Learn how to create lists that are optimized for wearable devices.
                                                                                                      Creating a 2D Picker
                                                                                                      Learn how to implement the 2D Picker UI pattern to navigate through pages of data.
                                                                                                      Showing Confirmations
                                                                                                      Learn how to display confirmation animations when users complete actions.
                                                                                                      Exiting Full-Screen Activities
                                                                                                      Learn how to implement the long-press-to-dismiss UI pattern to exit full-screen activities.
                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                      This class requires API level or higher

                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                      Sending and Syncing Data | Android Developers Skip to content

                                                                                                      Most visited

                                                                                                      Recently visited

                                                                                                      navigation

                                                                                                        Sending and Syncing Data

                                                                                                        Dependencies and prerequisites

                                                                                                        • Android 4.3 (API Level 18) or higher on the handset device
                                                                                                        • The latest version of Google Play services
                                                                                                        • An Android Wear device or Wear AVD

                                                                                                        The Wearable Data Layer API, which is part of Google Play services, provides a communication channel for your handheld and wearable apps. The API consists of a set of data objects that the system can send and synchronize over the wire and listeners that notify your apps of important events with the data layer:

                                                                                                        Data Items
                                                                                                        A DataItem provides data storage with automatic syncing between the handheld and wearable.
                                                                                                        Messages
                                                                                                        The MessageApi class can send messages and is good for remote procedure calls (RPC), such as controlling a handheld's media player from the wearable or starting an intent on the wearable from the handheld. Messages are also great for one-way requests or for a request/response communication model. If the handheld and wearable are connected, the system queues the message for delivery and returns a successful result code. If the devices are not connected, an error is returned. A successful result code does not indicate that the message was delivered successfully as the devices may disconnect after receiving the result code.

                                                                                                        Asset
                                                                                                        Asset objects are for sending binary blobs of data, such as images. You attach assets to data items and the system automatically takes care of the transfer for you, conserving Bluetooth bandwidth by caching large assets to avoid re-transmission.
                                                                                                        WearableListenerService (for services)

                                                                                                        Extending WearableListenerService lets you listen for important data layer events in a service. The system manages the lifecycle of the WearableListenerService, binding to the service when it needs to send data items or messages and unbinding the service when no work is needed.

                                                                                                        DataListener (for foreground activities)
                                                                                                        Implementing DataListener in an activity lets you listen for important data layer events when an activity is in the foreground. Using this instead of the WearableListenerService lets you listen for changes only when the user is actively using your app.
                                                                                                        Channel
                                                                                                        You can use the ChannelApi class to transfer large data items, such as music and movie files, from a handheld to a wearable device. The Channel API for data transfer has the following benefits:
                                                                                                        • Transfer large data files between two or more connected devices, without the automatic synchronization provided when using Asset objects attached to DataItem objects. The Channel API saves disk space unlike the DataApi class, which creates a copy of the assets on the local device before synchronizing with connected devices.
                                                                                                        • Reliably send a file that is too large in size to send using the MessageApi class.
                                                                                                        • Transfer streamed data, such as music pulled from a network server or voice data from the microphone.

                                                                                                        Warning: Because these APIs are designed for communication between handhelds and wearables, these are the only APIs you should use to set up communication between these devices. For instance, don't try to open low-level sockets to create a communication channel.

                                                                                                        Android Wear supports multiple wearables connected to a handheld device. For example, when the user saves a note on a handheld, it automatically appears on both of the user's Wear devices. To synchronize data between devices, Google’s servers host a cloud node in the network of devices. The system synchronizes data to directly connected devices, the cloud node, and to wearable devices connected to the cloud node via Wi-Fi.

                                                                                                        Figure 1. A sample network of nodes with handheld and wearable devices.

                                                                                                        Lessons

                                                                                                        Accessing the Wearable Data Layer
                                                                                                        This lesson shows you how to create a client to access the Data Layer APIs.
                                                                                                        Syncing Data Items
                                                                                                        Data items are objects that are stored in a replicated data store that is automatically synced from handhelds to wearables.
                                                                                                        Transferring Assets
                                                                                                        Assets are binary blobs of data that you typically use to transfer images or media.
                                                                                                        Sending and Receiving Messages
                                                                                                        Messages are designed for fire-and-forget messages that you can send back and forth between the wearable and handheld.
                                                                                                        Handling Data Layer Events
                                                                                                        Be notified of changes and events to the data layer.
                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                        This class requires API level or higher

                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                        Creating Watch Faces | Android Developers Skip to content

                                                                                                        Most visited

                                                                                                        Recently visited

                                                                                                        navigation

                                                                                                          Creating Watch Faces

                                                                                                          Dependencies and Prerequisites

                                                                                                          • Android 4.3 (API level 18) or higher on the handheld device
                                                                                                          • Android 5.0 (API level 21) or higher on the wearable device

                                                                                                          Design Guide

                                                                                                          Watch Faces

                                                                                                          Video

                                                                                                          DevBytes: Watch Faces for Android Wear

                                                                                                          Watch faces in Android Wear leverage a dynamic digital canvas to tell time using colors, animations, and relevant contextual information. The Android Wear companion app provides watch faces with different styles and shapes. When users select one of the available watch faces on the wearable or on the companion app, the wearable device previews the watch face and lets the user set configuration options.

                                                                                                          Android Wear enables you to create custom watch faces for Wear devices. When users install a handheld app that contains a wearable app with watch faces, they become available in the Android Wear companion app on the handheld device and in the watch face picker on the wearable device.

                                                                                                          This class teaches you to implement custom watch faces and to package them inside a wearable app. This class also covers design considerations and implementation tips to ensure that your designs integrate with system UI elements and are power-efficient.

                                                                                                          Note: We recommend using Android Studio for Android Wear development, as it provides project setup, library inclusion, and packaging conveniences. This training assumes you are using Android Studio.

                                                                                                          Lessons

                                                                                                          Designing Watch Faces
                                                                                                          Learn how to design a watch face that works on any Android Wear device.
                                                                                                          Building a Watch Face Service
                                                                                                          Learn how to respond to important events during the lifecycle of your watch face.
                                                                                                          Drawing Watch Faces
                                                                                                          Learn how to draw your watch face on a Wear device screen.
                                                                                                          Showing Information in Watch Faces
                                                                                                          Learn how to incorporate contextual information into your watch face.
                                                                                                          Creating Interactive Watch Faces
                                                                                                          Learn how to enable the user to interact with your watch face.
                                                                                                          Providing Configuration Activities
                                                                                                          Learn how to create watch faces with configurable parameters.
                                                                                                          Addressing Common Issues
                                                                                                          Learn how to fix common problems when developing a watch face.
                                                                                                          Optimizing Performance and Battery Life
                                                                                                          Learn how to improve the frame rate of your animations and how to save power.
                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                          This class requires API level or higher

                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                          Building Apps for TV | Android Developers Skip to content

                                                                                                          Most visited

                                                                                                          Recently visited

                                                                                                          navigation

                                                                                                            Building Apps for TV

                                                                                                            These classes teach you how to build apps for TV devices.

                                                                                                            Note: For details on how to publish your TV apps in Google Play, see Distribute to Android TV.

                                                                                                            If you prefer to learn through interactive video training, check out this online course about extending your apps to work with Android TV.

                                                                                                            Start the video course

                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                            This class requires API level or higher

                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                            Building TV Apps | Android Developers Skip to content

                                                                                                            Most visited

                                                                                                            Recently visited

                                                                                                            navigation

                                                                                                              Building TV Apps

                                                                                                              Dependencies and Prerequisites

                                                                                                              • Android 5.0 (API level 21) or higher

                                                                                                              Android offers a rich user experience that's optimized for apps running on large screen devices, such as high-definition televisions. Apps on TV offer new opportunities to delight your users from the comfort of their couch.

                                                                                                              TV apps use the same structure as those for phones and tablets. This approach means you can create new TV apps based on what you already know about building apps for Android, or extend your existing apps to also run on TV devices. However, the user interaction model for TV is substantially different from phone and tablet devices. In order to make your app successful on TV devices, you must design new layouts that can be easily understood from 10 feet away, and provide navigation that works with just a directional pad and a select button.

                                                                                                              This class describes how to start building apps for TV, including setting up your development environment, basic requirements for layouts and navigation, as well as guidance on how to handle hardware features that are not typically available on TV devices.

                                                                                                              Note: You are encouraged to use Android Studio for building TV apps, because it provides project setup, library inclusion, and packaging conveniences. This training assumes you are using Android Studio.

                                                                                                              Lessons

                                                                                                              Getting Started with TV Apps
                                                                                                              Learn how to create a new Android Studio project for TV apps or modify your existing app project to run on TV devices.
                                                                                                              Handling TV Hardware
                                                                                                              Learn how to check if your app is running on TV hardware, handle unsupported hardware features, and manage controller devices.
                                                                                                              Building TV Layouts
                                                                                                              Learn the minimum requirements for TV layouts and how to implement them.
                                                                                                              Creating TV Navigation
                                                                                                              Learn the requirements for TV navigation and how to implement TV-compatible navigation.
                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                              This class requires API level or higher

                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                              Building TV Playback Apps | Android Developers Skip to content

                                                                                                              Most visited

                                                                                                              Recently visited

                                                                                                              navigation

                                                                                                                Building TV Playback Apps

                                                                                                                Dependencies and Prerequisites

                                                                                                                • Android 5.0 (API level 21) or higher

                                                                                                                You should also read

                                                                                                                Video

                                                                                                                DevBytes: Android TV — Using the Leanback library

                                                                                                                Browsing and playing media files is frequently part of the user experience provided by a TV app. Building such an experience from scratch, while making sure that it is fast, fluid, and attractive can be quite challenging. Whether your app provides access to a small or large media catalog, it is important to allow users to quickly browse options and get to the content they want.

                                                                                                                The Android framework provides classes for building user interfaces for these types of apps with the v17 leanback support library. This library provides a framework of classes for creating an efficient and familiar interface for browsing and playing media files with minimal coding. The classes are designed to be extended and customized so you can create an experience that is unique to your app.

                                                                                                                This class shows you how to build a TV app for browsing and playing media content using the Leanback support libraries for TV.

                                                                                                                Topics

                                                                                                                Creating a Catalog Browser
                                                                                                                Learn how to use the Leanback support library to build a browsing interface for media catalogs.
                                                                                                                Providing a Card View
                                                                                                                Learn how to use the Leanback support library to build a card view for content items.
                                                                                                                Building a Details View
                                                                                                                Learn how to use the Leanback support library to build a details page for media items.
                                                                                                                Displaying a Now Playing Card
                                                                                                                Learn how to use a MediaSession to display a Now Playing card on the home screen.
                                                                                                                Adding a Guided Step
                                                                                                                Learn how to use the Leanback support library to guide a user through a series of decisions.
                                                                                                                Enabling Background Playback
                                                                                                                Learn how to continue playback when the user clicks on Home.
                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                This class requires API level or higher

                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                Creating a Catalog Browser | Android Developers Skip to content

                                                                                                                Most visited

                                                                                                                Recently visited

                                                                                                                navigation

                                                                                                                  Creating a Catalog Browser

                                                                                                                  A media app that runs on a TV needs to allow users to browse its content offerings, make a selection, and start playing content. The content browsing experience for apps of this type should be simple and intuitive, as well as visually pleasing and engaging.

                                                                                                                  This lesson discusses how to use the classes provided by the v17 leanback support library to implement a user interface for browsing music or videos from your app's media catalog.

                                                                                                                  App main screen

                                                                                                                  Figure 1. The Leanback sample app browse fragment displays video catalog data.

                                                                                                                  Create a Media Browse Layout

                                                                                                                  The BrowseFragment class in the leanback library allows you to create a primary layout for browsing categories and rows of media items with a minimum of code. The following example shows how to create a layout that contains a BrowseFragment object:

                                                                                                                  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                      android:id="@+id/main_frame"
                                                                                                                      android:layout_width="match_parent"
                                                                                                                      android:layout_height="match_parent">
                                                                                                                  
                                                                                                                      <fragment
                                                                                                                          android:name="com.example.android.tvleanback.ui.MainFragment"
                                                                                                                          android:id="@+id/main_browse_fragment"
                                                                                                                          android:layout_width="match_parent"
                                                                                                                          android:layout_height="match_parent" />
                                                                                                                  
                                                                                                                  </FrameLayout>
                                                                                                                  

                                                                                                                  The application's main activity sets this view, as shown in the following example:

                                                                                                                  public class MainActivity extends Activity {
                                                                                                                      @Override
                                                                                                                      public void onCreate(Bundle savedInstanceState) {
                                                                                                                          super.onCreate(savedInstanceState);
                                                                                                                          setContentView(R.layout.main);
                                                                                                                      }
                                                                                                                  ...
                                                                                                                  

                                                                                                                  The BrowseFragment methods populate the view with the video data and UI elements and set the layout parameters such as the icon, title, and whether category headers are enabled.

                                                                                                                  The application's subclass that implements the BrowseFragment methods also sets up event listeners for user actions on the UI elements, and prepares the background manager, as shown in the following example:

                                                                                                                  public class MainFragment extends BrowseFragment implements
                                                                                                                          LoaderManager.LoaderCallbacks<HashMap<String, List<Movie>>> {
                                                                                                                  
                                                                                                                  ...
                                                                                                                  
                                                                                                                      @Override
                                                                                                                      public void onActivityCreated(Bundle savedInstanceState) {
                                                                                                                          super.onActivityCreated(savedInstanceState);
                                                                                                                  
                                                                                                                          loadVideoData();
                                                                                                                  
                                                                                                                          prepareBackgroundManager();
                                                                                                                          setupUIElements();
                                                                                                                          setupEventListeners();
                                                                                                                      }
                                                                                                                  ...
                                                                                                                  
                                                                                                                      private void prepareBackgroundManager() {
                                                                                                                          mBackgroundManager = BackgroundManager.getInstance(getActivity());
                                                                                                                          mBackgroundManager.attach(getActivity().getWindow());
                                                                                                                          mDefaultBackground = getResources()
                                                                                                                              .getDrawable(R.drawable.default_background);
                                                                                                                          mMetrics = new DisplayMetrics();
                                                                                                                          getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
                                                                                                                      }
                                                                                                                  
                                                                                                                      private void setupUIElements() {
                                                                                                                          setBadgeDrawable(getActivity().getResources()
                                                                                                                              .getDrawable(R.drawable.videos_by_google_banner));
                                                                                                                          // Badge, when set, takes precedent over title
                                                                                                                          setTitle(getString(R.string.browse_title));
                                                                                                                          setHeadersState(HEADERS_ENABLED);
                                                                                                                          setHeadersTransitionOnBackEnabled(true);
                                                                                                                          // set headers background color
                                                                                                                          setBrandColor(getResources().getColor(R.color.fastlane_background));
                                                                                                                          // set search icon color
                                                                                                                          setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
                                                                                                                      }
                                                                                                                  
                                                                                                                      private void loadVideoData() {
                                                                                                                          VideoProvider.setContext(getActivity());
                                                                                                                          mVideosUrl = getActivity().getResources().getString(R.string.catalog_url);
                                                                                                                          getLoaderManager().initLoader(0, null, this);
                                                                                                                      }
                                                                                                                  
                                                                                                                      private void setupEventListeners() {
                                                                                                                          setOnSearchClickedListener(new View.OnClickListener() {
                                                                                                                  
                                                                                                                              @Override
                                                                                                                              public void onClick(View view) {
                                                                                                                                  Intent intent = new Intent(getActivity(), SearchActivity.class);
                                                                                                                                  startActivity(intent);
                                                                                                                              }
                                                                                                                          });
                                                                                                                  
                                                                                                                          setOnItemViewClickedListener(new ItemViewClickedListener());
                                                                                                                          setOnItemViewSelectedListener(new ItemViewSelectedListener());
                                                                                                                      }
                                                                                                                  ...
                                                                                                                  

                                                                                                                  Set UI Elements

                                                                                                                  In the sample above, the private method setupUIElements() calls several of the BrowseFragment methods to style the media catalog browser:

                                                                                                                  • setBadgeDrawable() places the specified drawable resource in the upper-right corner of the browse fragment, as shown in figures 1 and 2. This method replaces the title string with the drawable resource, if setTitle() is also called. The drawable resource should be 52dps tall.
                                                                                                                  • setTitle() sets the title string in the upper-right corner of the browse fragment, unless setBadgeDrawable() is called.
                                                                                                                  • setHeadersState() and setHeadersTransitionOnBackEnabled() hide or disable the headers. See Hide or Disable Headers for more information.
                                                                                                                  • setBrandColor() sets the background color for UI elements in the browse fragment, specifically the header section background color, with the specified color value.
                                                                                                                  • setSearchAffordanceColor() sets the color of the search icon with the specified color value. The search icon appears in the upper-left corner of the browse fragment, as shown in figures 1 and 2.

                                                                                                                  The browse fragment shown in figure 1 lists the video category names (the row headers) in the left pane. Text views display these category names from the video database. You can customize the header to include additional views in a more complex layout. The following sections show how to include an image view that displays an icon next to the category name, as shown in figure 2.

                                                                                                                  App main screen

                                                                                                                  Figure 2. The row headers in the browse fragment, with both an icon and a text label.

                                                                                                                  The layout for the row header is defined as follows:

                                                                                                                  <?xml version="1.0" encoding="utf-8"?>
                                                                                                                  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                      android:orientation="horizontal"
                                                                                                                      android:layout_width="match_parent"
                                                                                                                      android:layout_height="match_parent">
                                                                                                                  
                                                                                                                      <ImageView
                                                                                                                          android:id="@+id/header_icon"
                                                                                                                          android:layout_width="32dp"
                                                                                                                          android:layout_height="32dp" />
                                                                                                                      <TextView
                                                                                                                          android:id="@+id/header_label"
                                                                                                                          android:layout_marginTop="6dp"
                                                                                                                          android:layout_width="wrap_content"
                                                                                                                          android:layout_height="wrap_content" />
                                                                                                                  
                                                                                                                  </LinearLayout>
                                                                                                                  

                                                                                                                  Use a Presenter and implement the abstract methods to create, bind, and unbind the view holder. The following example shows how to bind the viewholder with two views, an ImageView and a TextView.

                                                                                                                  public class IconHeaderItemPresenter extends Presenter {
                                                                                                                      @Override
                                                                                                                      public ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
                                                                                                                          LayoutInflater inflater = (LayoutInflater) viewGroup.getContext()
                                                                                                                                  .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                                                                                                                  
                                                                                                                          View view = inflater.inflate(R.layout.icon_header_item, null);
                                                                                                                  
                                                                                                                          return new ViewHolder(view);
                                                                                                                      }
                                                                                                                  
                                                                                                                      @Override
                                                                                                                      public void onBindViewHolder(ViewHolder viewHolder, Object o) {
                                                                                                                          HeaderItem headerItem = ((ListRow) o).getHeaderItem();
                                                                                                                          View rootView = viewHolder.view;
                                                                                                                  
                                                                                                                          ImageView iconView = (ImageView) rootView.findViewById(R.id.header_icon);
                                                                                                                          Drawable icon = rootView.getResources().getDrawable(R.drawable.ic_action_video, null);
                                                                                                                          iconView.setImageDrawable(icon);
                                                                                                                  
                                                                                                                          TextView label = (TextView) rootView.findViewById(R.id.header_label);
                                                                                                                          label.setText(headerItem.getName());
                                                                                                                      }
                                                                                                                  
                                                                                                                      @Override
                                                                                                                      public void onUnbindViewHolder(ViewHolder viewHolder) {
                                                                                                                      // no op
                                                                                                                      }
                                                                                                                  }
                                                                                                                  

                                                                                                                  This example shows how to define the presenter for a complex layout with multiple views, and you could use this pattern to do something even more complex. However, an easier way to combine a TextView with a drawable resource is to use the TextView.drawableLeft attribute. Doing it this way, you don't need the ImageView shown here.

                                                                                                                  In the BrowseFragment implementation that displays the catalog browser, use the setHeaderPresenterSelector() method to set the presenter for the row header, as shown in the following example.

                                                                                                                  setHeaderPresenterSelector(new PresenterSelector() {
                                                                                                                      @Override
                                                                                                                      public Presenter getPresenter(Object o) {
                                                                                                                          return new IconHeaderItemPresenter();
                                                                                                                      }
                                                                                                                  });
                                                                                                                  

                                                                                                                  Hide or Disable Headers

                                                                                                                  Sometimes you may not want the row headers to appear: when there aren't enough categories to require a scrollable list, for example. Call the BrowseFragment.setHeadersState() method during the fragment's onActivityCreated() method to hide or disable the row headers. The setHeadersState() method sets the initial state of the headers in the browse fragment given one of the following constants as a parameter:

                                                                                                                  • HEADERS_ENABLED - When the browse fragment activity is created, the headers are enabled and shown by default. The headers appear as shown in figures 1 and 2 on this page.
                                                                                                                  • HEADERS_HIDDEN - When the browse fragment activity is created, headers are enabled and hidden by default. The header section of the screen is collapsed, as shown in figure 1 of Providing a Card View. The user can select the collapsed header section to expand it.
                                                                                                                  • HEADERS_DISABLED - When the browse fragment activity is created, headers are disabled by default and are never displayed.

                                                                                                                  If either HEADERS_ENABLED or HEADERS_HIDDEN is set, you can call setHeadersTransitionOnBackEnabled() to support moving back to the row header from a selected content item in the row. This is enabled by default (if you don't call the method), but if you want to handle the back movement yourself, you should pass the value false to setHeadersTransitionOnBackEnabled() and implement your own back stack handling.

                                                                                                                  Display Media Lists

                                                                                                                  The BrowseFragment class allows you to define and display browsable media content categories and media items from a media catalog using adapters and presenters. Adapters enable you to connect to local or online data sources that contain your media catalog information. Adapters use presenters to create views and bind data to those views for displaying an item on screen.

                                                                                                                  The following example code shows an implementation of a Presenter for displaying string data:

                                                                                                                  public class StringPresenter extends Presenter {
                                                                                                                      private static final String TAG = "StringPresenter";
                                                                                                                  
                                                                                                                      public ViewHolder onCreateViewHolder(ViewGroup parent) {
                                                                                                                          TextView textView = new TextView(parent.getContext());
                                                                                                                          textView.setFocusable(true);
                                                                                                                          textView.setFocusableInTouchMode(true);
                                                                                                                          textView.setBackground(
                                                                                                                                  parent.getContext().getResources().getDrawable(R.drawable.text_bg));
                                                                                                                          return new ViewHolder(textView);
                                                                                                                      }
                                                                                                                  
                                                                                                                      public void onBindViewHolder(ViewHolder viewHolder, Object item) {
                                                                                                                          ((TextView) viewHolder.view).setText(item.toString());
                                                                                                                      }
                                                                                                                  
                                                                                                                      public void onUnbindViewHolder(ViewHolder viewHolder) {
                                                                                                                          // no op
                                                                                                                      }
                                                                                                                  }
                                                                                                                  

                                                                                                                  Once you have constructed a presenter class for your media items, you can build an adapter and attach it to the BrowseFragment to display those items on screen for browsing by the user. The following example code demonstrates how to construct an adapter to display categories and items in those categories using the StringPresenter class shown in the previous code example:

                                                                                                                  private ArrayObjectAdapter mRowsAdapter;
                                                                                                                  private static final int NUM_ROWS = 4;
                                                                                                                  
                                                                                                                  @Override
                                                                                                                  protected void onCreate(Bundle savedInstanceState) {
                                                                                                                      ...
                                                                                                                  
                                                                                                                      buildRowsAdapter();
                                                                                                                  }
                                                                                                                  
                                                                                                                  private void buildRowsAdapter() {
                                                                                                                      mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
                                                                                                                  
                                                                                                                      for (int i = 0; i < NUM_ROWS; ++i) {
                                                                                                                          ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
                                                                                                                                  new StringPresenter());
                                                                                                                          listRowAdapter.add("Media Item 1");
                                                                                                                          listRowAdapter.add("Media Item 2");
                                                                                                                          listRowAdapter.add("Media Item 3");
                                                                                                                          HeaderItem header = new HeaderItem(i, "Category " + i);
                                                                                                                          mRowsAdapter.add(new ListRow(header, listRowAdapter));
                                                                                                                      }
                                                                                                                  
                                                                                                                      mBrowseFragment.setAdapter(mRowsAdapter);
                                                                                                                  }
                                                                                                                  

                                                                                                                  This example shows a static implementation of the adapters. A typical media browsing application uses data from an online database or web service. For an example of a browsing application that uses data retrieved from the web, see the Android Leanback sample app.

                                                                                                                  Update the Background

                                                                                                                  In order to add visual interest to a media-browsing app on TV, you can update the background image as users browse through content. This technique can make interaction with your app more cinematic and enjoyable.

                                                                                                                  The Leanback support library provides a BackgroundManager class for changing the background of your TV app activity. The following example shows how to create a simple method for updating the background within your TV app activity:

                                                                                                                  protected void updateBackground(Drawable drawable) {
                                                                                                                      BackgroundManager.getInstance(this).setDrawable(drawable);
                                                                                                                  }
                                                                                                                  

                                                                                                                  Many of the existing media-browse apps automatically update the background as the user navigates through media listings. In order to do this, you can set up a selection listener to automatically update the background based on the user's current selection. The following example shows you how to set up an OnItemViewSelectedListener class to catch selection events and update the background:

                                                                                                                  protected void clearBackground() {
                                                                                                                      BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
                                                                                                                  }
                                                                                                                  
                                                                                                                  protected OnItemViewSelectedListener getDefaultItemViewSelectedListener() {
                                                                                                                      return new OnItemViewSelectedListener() {
                                                                                                                          @Override
                                                                                                                          public void onItemSelected(Object item, Row row) {
                                                                                                                              if (item instanceof Movie ) {
                                                                                                                                  Drawable background = ((Movie)item).getBackdropDrawable();
                                                                                                                                  updateBackground(background);
                                                                                                                              } else {
                                                                                                                                  clearBackground();
                                                                                                                              }
                                                                                                                          }
                                                                                                                      };
                                                                                                                  }
                                                                                                                  

                                                                                                                  Note: The implementation above is a simple example shown for purposes of illustration. When creating this function in your own app, you should consider running the background update action in a separate thread for better performance. In addition, if you are planning on updating the background in response to users scrolling through items, consider adding a time to delay a background image update until the user settles on an item. This technique avoids excessive background image updates.

                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                  This class requires API level or higher

                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                  Building a Details View | Android Developers Skip to content

                                                                                                                  Most visited

                                                                                                                  Recently visited

                                                                                                                  navigation

                                                                                                                    Building a Details View

                                                                                                                    The media browsing interface classes provided by the v17 leanback support library include classes for displaying additional information about a media item, such as a description or reviews, and for taking action on that item, such as purchasing it or playing its content.

                                                                                                                    This lesson discusses how to create a presenter class for media item details, and how to extend the DetailsFragment class to implement a details view for a media item when it is selected by a user.

                                                                                                                    Note: The implementation example shown here uses an additional activity to contain the DetailsFragment. However, it is possible to avoid creating a second activity by replacing the current BrowseFragment with a DetailsFragment within the same activity using fragment transactions. For more information on using fragment transactions, see the Building a Dynamic UI with Fragments training.

                                                                                                                    Build a Details Presenter

                                                                                                                    In the media browsing framework provided by the leanback library, you use presenter objects to control the display of data on screen, including media item details. The framework provides the AbstractDetailsDescriptionPresenter class for this purpose, which is a nearly complete implementation of the presenter for media item details. All you have to do is implement the onBindDescription() method to bind the view fields to your data objects, as shown in the following code sample:

                                                                                                                    public class DetailsDescriptionPresenter
                                                                                                                            extends AbstractDetailsDescriptionPresenter {
                                                                                                                    
                                                                                                                        @Override
                                                                                                                        protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
                                                                                                                            MyMediaItemDetails details = (MyMediaItemDetails) itemData;
                                                                                                                            // In a production app, the itemData object contains the information
                                                                                                                            // needed to display details for the media item:
                                                                                                                            // viewHolder.getTitle().setText(details.getShortTitle());
                                                                                                                    
                                                                                                                            // Here we provide static data for testing purposes:
                                                                                                                            viewHolder.getTitle().setText(itemData.toString());
                                                                                                                            viewHolder.getSubtitle().setText("2014   Drama   TV-14");
                                                                                                                            viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
                                                                                                                                    + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
                                                                                                                                    + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
                                                                                                                                    + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
                                                                                                                                    + "commodo consequat.");
                                                                                                                        }
                                                                                                                    }
                                                                                                                    

                                                                                                                    Extend the Details Fragment

                                                                                                                    When using the DetailsFragment class for displaying your media item details, extend that class to provide additional content such as a preview image and actions for the media item. You can also provide additional content, such as a list of related media items.

                                                                                                                    The following example code demonstrates how to use the presenter class shown in the previous section, to add a preview image and actions for the media item being viewed. This example also shows the addition of a related media items row, which appears below the details listing.

                                                                                                                    public class MediaItemDetailsFragment extends DetailsFragment {
                                                                                                                        private static final String TAG = "MediaItemDetailsFragment";
                                                                                                                        private ArrayObjectAdapter mRowsAdapter;
                                                                                                                    
                                                                                                                        @Override
                                                                                                                        public void onCreate(Bundle savedInstanceState) {
                                                                                                                            Log.i(TAG, "onCreate");
                                                                                                                            super.onCreate(savedInstanceState);
                                                                                                                    
                                                                                                                            buildDetails();
                                                                                                                        }
                                                                                                                    
                                                                                                                        private void buildDetails() {
                                                                                                                            ClassPresenterSelector selector = new ClassPresenterSelector();
                                                                                                                            // Attach your media item details presenter to the row presenter:
                                                                                                                            FullWidthDetailsOverviewRowPresenter rowPresenter =
                                                                                                                                new FullWidthDetailsOverviewRowPresenter(
                                                                                                                                    new DetailsDescriptionPresenter());
                                                                                                                    
                                                                                                                            selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
                                                                                                                            selector.addClassPresenter(ListRow.class,
                                                                                                                                    new ListRowPresenter());
                                                                                                                            mRowsAdapter = new ArrayObjectAdapter(selector);
                                                                                                                    
                                                                                                                            Resources res = getActivity().getResources();
                                                                                                                            DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
                                                                                                                                    "Media Item Details");
                                                                                                                    
                                                                                                                            // Add images and action buttons to the details view
                                                                                                                            detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
                                                                                                                            detailsOverview.addAction(new Action(1, "Buy $9.99"));
                                                                                                                            detailsOverview.addAction(new Action(2, "Rent $2.99"));
                                                                                                                            mRowsAdapter.add(detailsOverview);
                                                                                                                    
                                                                                                                            // Add a Related items row
                                                                                                                            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
                                                                                                                                    new StringPresenter());
                                                                                                                            listRowAdapter.add("Media Item 1");
                                                                                                                            listRowAdapter.add("Media Item 2");
                                                                                                                            listRowAdapter.add("Media Item 3");
                                                                                                                            HeaderItem header = new HeaderItem(0, "Related Items", null);
                                                                                                                            mRowsAdapter.add(new ListRow(header, listRowAdapter));
                                                                                                                    
                                                                                                                            setAdapter(mRowsAdapter);
                                                                                                                        }
                                                                                                                    }
                                                                                                                    

                                                                                                                    Create a Details Activity

                                                                                                                    Fragments such as the DetailsFragment must be contained within an activity in order to be used for display. Creating an activity for your details view, separate from the browse activity, enables you to invoke your details view using an Intent. This section explains how to build an activity to contain your implementation of the detail view for your media items.

                                                                                                                    Start creating the details activity by building a layout that references your implementation of the DetailsFragment:

                                                                                                                    <!-- file: res/layout/details.xml -->
                                                                                                                    
                                                                                                                    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                        android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"
                                                                                                                        android:id="@+id/details_fragment"
                                                                                                                        android:layout_width="match_parent"
                                                                                                                        android:layout_height="match_parent"
                                                                                                                    />
                                                                                                                    

                                                                                                                    Next, create an activity class that uses the layout shown in the previous code example:

                                                                                                                    public class DetailsActivity extends Activity
                                                                                                                    {
                                                                                                                        @Override
                                                                                                                        public void onCreate(Bundle savedInstanceState) {
                                                                                                                            super.onCreate(savedInstanceState);
                                                                                                                            setContentView(R.layout.details);
                                                                                                                        }
                                                                                                                    }
                                                                                                                    

                                                                                                                    Finally, add this new activity to the manifest. Remember to apply the Leanback theme to ensure that the user interface is consistent with the media browse activity:

                                                                                                                    <application>
                                                                                                                      ...
                                                                                                                    
                                                                                                                      <activity android:name=".DetailsActivity"
                                                                                                                        android:exported="true"
                                                                                                                        android:theme="@style/Theme.Leanback"/>
                                                                                                                    
                                                                                                                    </application>
                                                                                                                    

                                                                                                                    Define a Listener for Clicked Items

                                                                                                                    After you have implemented the DetailsFragment, modify your main media browsing view to move to your details view when a user clicks on a media item. In order to enable this behavior, add an OnItemViewClickedListener object to the BrowseFragment that fires an intent to start the item details activity.

                                                                                                                    The following example shows how to implement a listener to start the details view when a user clicks a media item in the main media browsing activity:

                                                                                                                    public class BrowseMediaActivity extends Activity {
                                                                                                                        ...
                                                                                                                    
                                                                                                                        @Override
                                                                                                                        protected void onCreate(Bundle savedInstanceState) {
                                                                                                                            ...
                                                                                                                    
                                                                                                                            // create the media item rows
                                                                                                                            buildRowsAdapter();
                                                                                                                    
                                                                                                                            // add a listener for selected items
                                                                                                                            mBrowseFragment.OnItemViewClickedListener(
                                                                                                                                new OnItemViewClickedListener() {
                                                                                                                                    @Override
                                                                                                                                    public void onItemClicked(Object item, Row row) {
                                                                                                                                        System.out.println("Media Item clicked: " + item.toString());
                                                                                                                                        Intent intent = new Intent(BrowseMediaActivity.this,
                                                                                                                                                DetailsActivity.class);
                                                                                                                                        // pass the item information
                                                                                                                                        intent.getExtras().putLong("id", item.getId());
                                                                                                                                        startActivity(intent);
                                                                                                                                    }
                                                                                                                                });
                                                                                                                        }
                                                                                                                    }
                                                                                                                    
                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                    This class requires API level or higher

                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                    Building a Details View | Android Developers Skip to content

                                                                                                                    Most visited

                                                                                                                    Recently visited

                                                                                                                    navigation

                                                                                                                      Building a Details View

                                                                                                                      The media browsing interface classes provided by the v17 leanback support library include classes for displaying additional information about a media item, such as a description or reviews, and for taking action on that item, such as purchasing it or playing its content.

                                                                                                                      This lesson discusses how to create a presenter class for media item details, and how to extend the DetailsFragment class to implement a details view for a media item when it is selected by a user.

                                                                                                                      Note: The implementation example shown here uses an additional activity to contain the DetailsFragment. However, it is possible to avoid creating a second activity by replacing the current BrowseFragment with a DetailsFragment within the same activity using fragment transactions. For more information on using fragment transactions, see the Building a Dynamic UI with Fragments training.

                                                                                                                      Build a Details Presenter

                                                                                                                      In the media browsing framework provided by the leanback library, you use presenter objects to control the display of data on screen, including media item details. The framework provides the AbstractDetailsDescriptionPresenter class for this purpose, which is a nearly complete implementation of the presenter for media item details. All you have to do is implement the onBindDescription() method to bind the view fields to your data objects, as shown in the following code sample:

                                                                                                                      public class DetailsDescriptionPresenter
                                                                                                                              extends AbstractDetailsDescriptionPresenter {
                                                                                                                      
                                                                                                                          @Override
                                                                                                                          protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
                                                                                                                              MyMediaItemDetails details = (MyMediaItemDetails) itemData;
                                                                                                                              // In a production app, the itemData object contains the information
                                                                                                                              // needed to display details for the media item:
                                                                                                                              // viewHolder.getTitle().setText(details.getShortTitle());
                                                                                                                      
                                                                                                                              // Here we provide static data for testing purposes:
                                                                                                                              viewHolder.getTitle().setText(itemData.toString());
                                                                                                                              viewHolder.getSubtitle().setText("2014   Drama   TV-14");
                                                                                                                              viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
                                                                                                                                      + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
                                                                                                                                      + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
                                                                                                                                      + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
                                                                                                                                      + "commodo consequat.");
                                                                                                                          }
                                                                                                                      }
                                                                                                                      

                                                                                                                      Extend the Details Fragment

                                                                                                                      When using the DetailsFragment class for displaying your media item details, extend that class to provide additional content such as a preview image and actions for the media item. You can also provide additional content, such as a list of related media items.

                                                                                                                      The following example code demonstrates how to use the presenter class shown in the previous section, to add a preview image and actions for the media item being viewed. This example also shows the addition of a related media items row, which appears below the details listing.

                                                                                                                      public class MediaItemDetailsFragment extends DetailsFragment {
                                                                                                                          private static final String TAG = "MediaItemDetailsFragment";
                                                                                                                          private ArrayObjectAdapter mRowsAdapter;
                                                                                                                      
                                                                                                                          @Override
                                                                                                                          public void onCreate(Bundle savedInstanceState) {
                                                                                                                              Log.i(TAG, "onCreate");
                                                                                                                              super.onCreate(savedInstanceState);
                                                                                                                      
                                                                                                                              buildDetails();
                                                                                                                          }
                                                                                                                      
                                                                                                                          private void buildDetails() {
                                                                                                                              ClassPresenterSelector selector = new ClassPresenterSelector();
                                                                                                                              // Attach your media item details presenter to the row presenter:
                                                                                                                              FullWidthDetailsOverviewRowPresenter rowPresenter =
                                                                                                                                  new FullWidthDetailsOverviewRowPresenter(
                                                                                                                                      new DetailsDescriptionPresenter());
                                                                                                                      
                                                                                                                              selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
                                                                                                                              selector.addClassPresenter(ListRow.class,
                                                                                                                                      new ListRowPresenter());
                                                                                                                              mRowsAdapter = new ArrayObjectAdapter(selector);
                                                                                                                      
                                                                                                                              Resources res = getActivity().getResources();
                                                                                                                              DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
                                                                                                                                      "Media Item Details");
                                                                                                                      
                                                                                                                              // Add images and action buttons to the details view
                                                                                                                              detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
                                                                                                                              detailsOverview.addAction(new Action(1, "Buy $9.99"));
                                                                                                                              detailsOverview.addAction(new Action(2, "Rent $2.99"));
                                                                                                                              mRowsAdapter.add(detailsOverview);
                                                                                                                      
                                                                                                                              // Add a Related items row
                                                                                                                              ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
                                                                                                                                      new StringPresenter());
                                                                                                                              listRowAdapter.add("Media Item 1");
                                                                                                                              listRowAdapter.add("Media Item 2");
                                                                                                                              listRowAdapter.add("Media Item 3");
                                                                                                                              HeaderItem header = new HeaderItem(0, "Related Items", null);
                                                                                                                              mRowsAdapter.add(new ListRow(header, listRowAdapter));
                                                                                                                      
                                                                                                                              setAdapter(mRowsAdapter);
                                                                                                                          }
                                                                                                                      }
                                                                                                                      

                                                                                                                      Create a Details Activity

                                                                                                                      Fragments such as the DetailsFragment must be contained within an activity in order to be used for display. Creating an activity for your details view, separate from the browse activity, enables you to invoke your details view using an Intent. This section explains how to build an activity to contain your implementation of the detail view for your media items.

                                                                                                                      Start creating the details activity by building a layout that references your implementation of the DetailsFragment:

                                                                                                                      <!-- file: res/layout/details.xml -->
                                                                                                                      
                                                                                                                      <fragment xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                          android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"
                                                                                                                          android:id="@+id/details_fragment"
                                                                                                                          android:layout_width="match_parent"
                                                                                                                          android:layout_height="match_parent"
                                                                                                                      />
                                                                                                                      

                                                                                                                      Next, create an activity class that uses the layout shown in the previous code example:

                                                                                                                      public class DetailsActivity extends Activity
                                                                                                                      {
                                                                                                                          @Override
                                                                                                                          public void onCreate(Bundle savedInstanceState) {
                                                                                                                              super.onCreate(savedInstanceState);
                                                                                                                              setContentView(R.layout.details);
                                                                                                                          }
                                                                                                                      }
                                                                                                                      

                                                                                                                      Finally, add this new activity to the manifest. Remember to apply the Leanback theme to ensure that the user interface is consistent with the media browse activity:

                                                                                                                      <application>
                                                                                                                        ...
                                                                                                                      
                                                                                                                        <activity android:name=".DetailsActivity"
                                                                                                                          android:exported="true"
                                                                                                                          android:theme="@style/Theme.Leanback"/>
                                                                                                                      
                                                                                                                      </application>
                                                                                                                      

                                                                                                                      Define a Listener for Clicked Items

                                                                                                                      After you have implemented the DetailsFragment, modify your main media browsing view to move to your details view when a user clicks on a media item. In order to enable this behavior, add an OnItemViewClickedListener object to the BrowseFragment that fires an intent to start the item details activity.

                                                                                                                      The following example shows how to implement a listener to start the details view when a user clicks a media item in the main media browsing activity:

                                                                                                                      public class BrowseMediaActivity extends Activity {
                                                                                                                          ...
                                                                                                                      
                                                                                                                          @Override
                                                                                                                          protected void onCreate(Bundle savedInstanceState) {
                                                                                                                              ...
                                                                                                                      
                                                                                                                              // create the media item rows
                                                                                                                              buildRowsAdapter();
                                                                                                                      
                                                                                                                              // add a listener for selected items
                                                                                                                              mBrowseFragment.OnItemViewClickedListener(
                                                                                                                                  new OnItemViewClickedListener() {
                                                                                                                                      @Override
                                                                                                                                      public void onItemClicked(Object item, Row row) {
                                                                                                                                          System.out.println("Media Item clicked: " + item.toString());
                                                                                                                                          Intent intent = new Intent(BrowseMediaActivity.this,
                                                                                                                                                  DetailsActivity.class);
                                                                                                                                          // pass the item information
                                                                                                                                          intent.getExtras().putLong("id", item.getId());
                                                                                                                                          startActivity(intent);
                                                                                                                                      }
                                                                                                                                  });
                                                                                                                          }
                                                                                                                      }
                                                                                                                      
                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                      This class requires API level or higher

                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                      Displaying a Now Playing Card | Android Developers Skip to content

                                                                                                                      Most visited

                                                                                                                      Recently visited

                                                                                                                      navigation

                                                                                                                        Displaying a Now Playing Card

                                                                                                                        TV apps must display a Now Playing card when playing media behind the launcher or in the background. This card allows users to return to the app that is currently playing media.

                                                                                                                        The Android framework displays a Now Playing card on the home screen when there is an active MediaSession. The card includes media metadata such as album art, title, and app icon. When the user selects the card, the system opens the app.

                                                                                                                        This lesson shows how to use the MediaSession class to implement the Now Playing card.

                                                                                                                        Figure 1. Display a Now Playing card when playing media in the background.

                                                                                                                        Start a Media Session

                                                                                                                        Create a MediaSession when your app is preparing to play media. The following code snippet is an example of how to set the appropriate callback and flags:

                                                                                                                        mSession = new MediaSession(this, "MusicService");
                                                                                                                        mSession.setCallback(new MediaSessionCallback());
                                                                                                                        mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
                                                                                                                                MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
                                                                                                                        

                                                                                                                        Note: The Now Playing card will display only for a media session with the FLAG_HANDLES_TRANSPORT_CONTROLS flag set.

                                                                                                                        Display a Now Playing Card

                                                                                                                        The Now Playing card only appears for active sessions. You must call setActive(true) when playback begins. Your app must also request audio focus, as described in Managing Audio Focus.

                                                                                                                        private void handlePlayRequest() {
                                                                                                                        
                                                                                                                            tryToGetAudioFocus();
                                                                                                                        
                                                                                                                            if (!mSession.isActive()) {
                                                                                                                                mSession.setActive(true);
                                                                                                                            }
                                                                                                                        ...
                                                                                                                        

                                                                                                                        The card is removed from the launcher screen when a setActive(false) call deactivates the media session, or when another app initiates media playback. If playback is completely stopped and there is no active media, your app should deactivate the media session immediately. If playback is paused, your app should deactivate the media session after a delay, usually between 5 to 30 minutes.

                                                                                                                        Update the Playback State

                                                                                                                        Update the playback state in the MediaSession so the card can show the state of the current media.

                                                                                                                        private void updatePlaybackState() {
                                                                                                                            long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
                                                                                                                            if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                                                                                                                                position = mMediaPlayer.getCurrentPosition();
                                                                                                                            }
                                                                                                                            PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
                                                                                                                                    .setActions(getAvailableActions());
                                                                                                                            stateBuilder.setState(mState, position, 1.0f);
                                                                                                                            mSession.setPlaybackState(stateBuilder.build());
                                                                                                                        }
                                                                                                                        
                                                                                                                        private long getAvailableActions() {
                                                                                                                            long actions = PlaybackState.ACTION_PLAY_PAUSE |
                                                                                                                                    PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
                                                                                                                                    PlaybackState.ACTION_PLAY_FROM_SEARCH;
                                                                                                                            if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
                                                                                                                                return actions;
                                                                                                                            }
                                                                                                                            if (mState == PlaybackState.STATE_PLAYING) {
                                                                                                                                actions |= PlaybackState.ACTION_PAUSE;
                                                                                                                            } else {
                                                                                                                                actions |= PlaybackState.ACTION_PLAY;
                                                                                                                            }
                                                                                                                            if (mCurrentIndexOnQueue > 0) {
                                                                                                                                actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
                                                                                                                            }
                                                                                                                            if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) {
                                                                                                                                actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
                                                                                                                            }
                                                                                                                            return actions;
                                                                                                                        }
                                                                                                                        

                                                                                                                        Display the Media Metadata

                                                                                                                        Set the MediaMetadata with the setMetadata() method. This method of the media session object lets you provide information to the Now Playing card about the track such as the title, subtitle, and various icons. The following example assumes your track's data is stored in a custom data class, MediaData.

                                                                                                                        private void updateMetadata(MediaData myData) {
                                                                                                                            MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
                                                                                                                            // To provide most control over how an item is displayed set the
                                                                                                                            // display fields in the metadata
                                                                                                                            metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
                                                                                                                                    myData.displayTitle);
                                                                                                                            metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
                                                                                                                                    myData.displaySubtitle);
                                                                                                                            metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI,
                                                                                                                                    myData.artUri);
                                                                                                                            // And at minimum the title and artist for legacy support
                                                                                                                            metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE,
                                                                                                                                    myData.title);
                                                                                                                            metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST,
                                                                                                                                    myData.artist);
                                                                                                                            // A small bitmap for the artwork is also recommended
                                                                                                                            metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ART,
                                                                                                                                    myData.artBitmap);
                                                                                                                            // Add any other fields you have for your data as well
                                                                                                                            mSession.setMetadata(metadataBuilder.build());
                                                                                                                        }
                                                                                                                        

                                                                                                                        Respond to User Action

                                                                                                                        When the user selects the Now Playing card, the system opens the app that owns the session. If your app provides a PendingIntent to setSessionActivity(), the system launches the activity you specify, as demonstrated below. If not, the default system intent opens. The activity you specify must provide playback controls that allow users to pause or stop playback.

                                                                                                                        Intent intent = new Intent(mContext, MyActivity.class);
                                                                                                                            PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/,
                                                                                                                                    intent, PendingIntent.FLAG_UPDATE_CURRENT);
                                                                                                                            mSession.setSessionActivity(pi);
                                                                                                                        
                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                        This class requires API level or higher

                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                        Adding a Guided Step | Android Developers Skip to content

                                                                                                                        Most visited

                                                                                                                        Recently visited

                                                                                                                        navigation

                                                                                                                          Adding a Guided Step

                                                                                                                          Your application might have multi-step tasks for users. For example, your app might need to guide users through purchasing additional content, or setting up a complex configuration setting, or simply confirming a decision. All of these tasks require walking users through one or more ordered steps or decisions.

                                                                                                                          The v17 Leanback support library provides classes to implement multi-step user tasks. This lesson discusses how to use the GuidedStepFragment class to guide a user through a series of decisions to accomplish a task. GuidedStepFragment uses TV UI best practices to make multi-step tasks easy to understand and navigate on TV devices.

                                                                                                                          Provide Details for a Step

                                                                                                                          A GuidedStepFragment represents a single step in a series of steps. Visually it provides a guidance view on the left with step information. On the right, GuidedStepFragment provides a view containing a list of possible actions or decisions for this step.

                                                                                                                          Figure 1. An example guided step.

                                                                                                                          For each step in your multi-step task, extend GuidedStepFragment and provide context information about the step and actions the user can take. Override onCreateGuidance() and return a new GuidanceStylist.Guidance that contains context information, such as the step title, description, and icon.

                                                                                                                          @Override
                                                                                                                          public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
                                                                                                                              String title = getString(R.string.guidedstep_first_title);
                                                                                                                              String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
                                                                                                                              String description = getString(R.string.guidedstep_first_description);
                                                                                                                              Drawable icon = getActivity().getDrawable(R.drawable.guidedstep_main_icon_1);
                                                                                                                              return new GuidanceStylist.Guidance(title, description, breadcrumb, icon);
                                                                                                                          }
                                                                                                                          

                                                                                                                          Add your GuidedStepFragment subclass to your desired activity by calling GuidedStepFragment.add() in your activity’s onCreate() method. If your activity contains only GuidedStepFragment objects, use GuidedStepFragment.addAsRoot() instead of add() to add the first GuidedStepFragment. Using addAsRoot() ensures that if the user presses the Back button on the TV remote when viewing the first GuidedStepFragment, both the GuidedStepFragment and the parent activity will close.

                                                                                                                          Note: Add GuidedStepFragment objects programmatically and not in your layout XML files.

                                                                                                                          Create and Handle User Actions

                                                                                                                          Add user actions by overriding onCreateActions(). In your override, add a new GuidedAction for each action item, and provide the action string, description, and ID. Use GuidedAction.Builder to add new actions.

                                                                                                                          @Override
                                                                                                                          public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
                                                                                                                              // Add "Continue" user action for this step
                                                                                                                              actions.add(new GuidedAction.Builder()
                                                                                                                                     .id(CONTINUE)
                                                                                                                                     .title(getString(R.string.guidedstep_continue))
                                                                                                                                     .description(getString(R.string.guidedstep_letsdoit))
                                                                                                                                     .hasNext(true)
                                                                                                                                     .build());
                                                                                                                          ...
                                                                                                                          

                                                                                                                          Actions aren't limited to single-line selections. Here are additional types of actions you can create:

                                                                                                                          • Add an information label action by setting infoOnly(true). If you set infoOnly to true, the user can't select the action. To provide additional information about user choices, use label actions.
                                                                                                                          • Add an editable text action by setting editable(true). If editable is true, when the action is selected the user can enter text using the remote or a connected keyboard. Override onGuidedActionEdited() or onGuidedActionEditedAndProceed() to get the modified text the user entered.
                                                                                                                          • Add a set of actions that behave as checkable radio buttons by using checkSetId() with a common ID value to group actions into a set. All actions in the same list with the same check-set ID are considered linked. When the user selects one of the actions within that set, that action becomes checked, while all other actions become unchecked.
                                                                                                                          • Add a date-picker action by using GuidedDatePickerAction.Builder instead of GuidedAction.Builder in onCreateActions(). Override onGuidedActionEdited() or onGuidedActionEditedAndProceed() to get the modified date value the user entered.
                                                                                                                          • Add an action that uses subactions to let the user pick from an extended list of choices. Subactions are described in Add subactions.
                                                                                                                          • Add a button action that appears to the right of the actions list and is easily accessible. Button actions are described in Add button actions.

                                                                                                                          You can also add a visual indicator—to indicate that selecting the action leads to a new step—by setting hasNext(true). For all the different attributes that you can set, see GuidedAction.

                                                                                                                          To respond to actions, override onGuidedActionClicked() and process the passed-in GuidedAction. Identify the selected action by examining GuidedAction.getId().

                                                                                                                          Add subactions

                                                                                                                          Some actions might require giving the user an additional set of choices. A GuidedAction can specify a list of subactions that get displayed as a drop-down list of child actions.

                                                                                                                          Figure 2. Guided step subactions.

                                                                                                                          The subaction list can contain regular actions or radio button actions, but not date-picker or editable text actions. Also, a subaction cannot have its own set of subactions as the system does not support more than one level of subactions. Deeply nested sets of actions create a poor user experience.

                                                                                                                          To add subactions, first create and populate a list of GuidedActions that will act as subactions:

                                                                                                                          List<GuidedAction> subActions = new ArrayList<GuidedAction>();
                                                                                                                          subActions.add(new GuidedAction.Builder()
                                                                                                                                 .id(SUBACTION1)
                                                                                                                                 .title(getString(R.string.guidedstep_subaction1_title))
                                                                                                                                 .description(getString(R.string.guidedstep_subaction1_desc))
                                                                                                                                 .build());
                                                                                                                          ...
                                                                                                                          

                                                                                                                          In onCreateActions(), create a top-level GuidedAction that will display the list of subactions when selected:

                                                                                                                          @Override
                                                                                                                          public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
                                                                                                                          ...
                                                                                                                              actions.add(new GuidedAction.Builder()
                                                                                                                                     .id(SUBACTIONS)
                                                                                                                                     .title(getString(R.string.guidedstep_subactions_title))
                                                                                                                                     .description(getString(R.string.guidedstep_subactions_desc))
                                                                                                                                     .subActions(subActions)
                                                                                                                                     .build());
                                                                                                                          ...
                                                                                                                          }
                                                                                                                          

                                                                                                                          Finally, respond to subaction selections by overriding onSubGuidedActionClicked():

                                                                                                                          @Override
                                                                                                                          public boolean onSubGuidedActionClicked(GuidedAction action) {
                                                                                                                             // Check for which action was clicked, and handle as needed
                                                                                                                             if (action.getId() == SUBACTION1) {
                                                                                                                                 // Subaction 1 selected
                                                                                                                             }
                                                                                                                             // Return true to collapse the subactions drop-down list, or
                                                                                                                             // false to keep the drop-down list expanded.
                                                                                                                             return true;
                                                                                                                          }
                                                                                                                          

                                                                                                                          Add button actions

                                                                                                                          If your guided step has a large list of actions, users may have to scroll through the list to access the most commonly used actions. Use button actions to separate commonly used actions from the action list. Button actions appear to the right of the action list and are easy to navigate to.

                                                                                                                          Figure 3. Guided step button actions.

                                                                                                                          Button actions are created and handled just like regular actions, but you create button actions in onCreateButtonActions() instead of onCreateActions(). Respond to button actions in onGuidedActionClicked().

                                                                                                                          Use button actions for simple actions, such as navigation actions between steps. Don't use the date-picker action or other editable actions as button actions. Also, button actions cannot have subactions.

                                                                                                                          Group Guided Steps Into a Guided Sequence

                                                                                                                          A GuidedStepFragment represents a single step, however you might have several steps in an ordered sequence. Group multiple GuidedStepFragment objects together by using GuidedStepFragment.add() to add the next step in the sequence to the fragment stack.

                                                                                                                          @Override
                                                                                                                          public void onGuidedActionClicked(GuidedAction action) {
                                                                                                                              FragmentManager fm = getFragmentManager();
                                                                                                                              if (action.getId() == CONTINUE) {
                                                                                                                                 GuidedStepFragment.add(fm, new SecondStepFragment());
                                                                                                                              }
                                                                                                                          ...
                                                                                                                          

                                                                                                                          If the user presses the Back button on the TV remote, the device shows the previous GuidedStepFragment on the fragment stack. If you decide to provide your own GuidedAction that returns to the previous step, you can implement the Back behavior by calling getFragmentManager().popBackStack(). If you need to return the user to an even earlier step in the sequence, use popBackStackToGuidedStepFragment() to return to a specific GuidedStepFragment in the fragment stack.

                                                                                                                          When the user has finished the last step in the sequence, use finishGuidedStepFragments() to remove all GuidedStepFragments from the current stack and return to the original parent activity. If the first GuidedStepFragment was added using addAsRoot(), calling finishGuidedStepFragments() will also close the parent activity.

                                                                                                                          Customize Step Presentation

                                                                                                                          The GuidedStepFragment class can use custom themes that control presentation aspects such as title text formatting or step transition animations. Custom themes must inherit from Theme_Leanback_GuidedStep, and can provide overriding values for attributes defined in GuidanceStylist and GuidedActionsStylist.

                                                                                                                          To apply a custom theme to your GuidedStepFragment, do one of the following:

                                                                                                                          • Apply the theme to the parent activity by setting the android:theme attribute to the activity element in the Android manifest. Setting this attribute applies the theme to all child views and is the easiest way to apply a custom theme if the parent activity contains only GuidedStepFragment objects.
                                                                                                                          • If your activity already uses a custom theme and you don’t want to apply GuidedStepFragment styles to other views in the activity, add the LeanbackGuidedStepTheme_guidedStepTheme attribute to your existing custom activity theme. This attribute points to the custom theme that only the GuidedStepFragment objects in your activity use.
                                                                                                                          • If you use GuidedStepFragment objects in different activities that are part of the same overall multi-step task and want to use a consistent visual theme across all steps, override GuidedStepFragment.onProvideTheme() and return your custom theme.

                                                                                                                          For more information on how to add styles and themes, see Styles and Themes.

                                                                                                                          The GuidedStepFragment class uses special stylist classes to access and apply theme attributes. The GuidanceStylist class uses theme information to control presentation of the left guidance view, while the GuidedActionsStylist class uses theme information to control presentation of the right actions view.

                                                                                                                          To customize the visual style of your steps beyond what theme customization can provide, subclass GuidanceStylist or GuidedActionsStylist and return your subclass in GuidedStepFragment.onCreateGuidanceStylist() or GuidedStepFragment.onCreateActionsStylist(). For details on what you can customize in these subclasses, see the documentation on GuidanceStylist and GuidedActionsStylist.

                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                          This class requires API level or higher

                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                          Enabling Background Playback | Android Developers Skip to content

                                                                                                                          Most visited

                                                                                                                          Recently visited

                                                                                                                          navigation

                                                                                                                            Enabling Background Playback

                                                                                                                            This lesson teaches you to

                                                                                                                            1. Request Background Playback

                                                                                                                            A user watching content on a TV device may decide to switch to the TV launcher at any time. If a user switches to the launcher while using a TV playback app, by default the app is paused. Since the user did not explicitly request to pause playback, the default behavior might seem abrupt and unexpected. This lesson describes how to enable background playback in your app, which provides a better user experience.

                                                                                                                            Request Background Playback

                                                                                                                            Normally, when the user clicks on Home to display the TV launcher, the activity pauses. However, your app can request background playback, in which the activity continues playing behind the TV launcher.

                                                                                                                            To request background playback, call requestVisibleBehind(). Be sure to clean up media resources if the activity stops being visible. For example, you should free media resources if requestVisibleBehind() returns false to indicate that the request failed, or if the system calls your override of onVisibleBehindCanceled().

                                                                                                                            @Override
                                                                                                                            public void onPause() {
                                                                                                                              super.onPause();
                                                                                                                              if (mVideoView.isPlaying()) {
                                                                                                                                // Argument equals true to notify the system that the activity
                                                                                                                                // wishes to be visible behind other translucent activities
                                                                                                                                if (! requestVisibleBehind(true)) {
                                                                                                                                  // App-specific method to stop playback and release resources
                                                                                                                                  // because call to requestVisibleBehind(true) failed
                                                                                                                                  stopPlayback();
                                                                                                                                }
                                                                                                                              } else {
                                                                                                                                // Argument equals false because the activity is not playing
                                                                                                                                requestVisibleBehind(false);
                                                                                                                              }
                                                                                                                            }
                                                                                                                            
                                                                                                                            @Override
                                                                                                                            public void onVisibleBehindCanceled() {
                                                                                                                              // App-specific method to stop playback and release resources
                                                                                                                              stopPlayback();
                                                                                                                              super.onVisibleBehindCanceled();
                                                                                                                            }
                                                                                                                            
                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                            This class requires API level or higher

                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                            Helping Users Find Your Content on TV | Android Developers Skip to content

                                                                                                                            Most visited

                                                                                                                            Recently visited

                                                                                                                            navigation

                                                                                                                              Helping Users Find Your Content on TV

                                                                                                                              Dependencies and Prerequisites

                                                                                                                              • Android 5.0 (API level 21) or higher

                                                                                                                              You should also read

                                                                                                                              TV devices offer many entertainment options for users. They have thousands of content options from apps and related content services. At the same time, most users prefer to use TVs with the least amount of input possible. With the number of choices available to users, it is important for app developers to provide quick and easy paths for users to discover and enjoy your content.

                                                                                                                              The Android framework helps you provide a number of paths for users to discover your content, including recommendations on the home screen and searching within your app's content catalog.

                                                                                                                              This class shows you how to help users discover your app's content through recommendations and in-app searching.

                                                                                                                              Topics

                                                                                                                              Recommending TV Content
                                                                                                                              Learn how to recommend content for users so that it appears in the recommendations row on the home screen of a TV device.
                                                                                                                              Making TV Apps Searchable
                                                                                                                              Learn how to make your content searchable from the Android TV home screen.
                                                                                                                              Searching within TV Apps
                                                                                                                              Learn how to use a built-for-TV user interface for searching within your app.
                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                              This class requires API level or higher

                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                              Recommending TV Content | Android Developers Skip to content

                                                                                                                              Most visited

                                                                                                                              Recently visited

                                                                                                                              navigation

                                                                                                                                Recommending TV Content

                                                                                                                                When interacting with TVs, users generally prefer to give minimal input before watching content. An ideal scenario for many TV users is: sit down, turn on, and watch. The fewest steps to get users to content they enjoy is generally the path they prefer.

                                                                                                                                The Android framework assists with minimum-input interaction by providing a recommendations row on the home screen. Content recommendations appear as the first row of the TV home screen after the first use of the device. Contributing recommendations from your app's content catalog can help bring users back to your app.

                                                                                                                                Figure 1. An example of the recommendations row.

                                                                                                                                This lesson teaches you how to create recommendations and provide them to the Android framework so users can easily discover and enjoy your app content. This discussion describes some code from the Android Leanback sample app.

                                                                                                                                Best Practices for Recommendations

                                                                                                                                Recommendations help users quickly find the content and apps they enjoy. Creating recommendations that are high-quality and relevant to users is an important factor in creating a great user experience with your TV app. For this reason, you should carefully consider what recommendations you present to the user and manage them closely.

                                                                                                                                Types of Recommendations

                                                                                                                                When you create recommendations, you should link users back to incomplete viewing activities or suggest activities that extend that to related content. Here are some specific type of recommendations you should consider:

                                                                                                                                • Continuation content recommendations for the next episode for users to resume watching a series. Or, use continuation recommendations for paused movies, TV shows, or podcasts so users can get back to watching paused content in just a few clicks.
                                                                                                                                • New content recommendations, such as for a new first-run episode, if the user finished watching another series. Also, if your app lets users subscribe to, follow, or track content, use new content recommendations for unwatched items in their list of tracked content.
                                                                                                                                • Related content recommendations based on the users' historic viewing behavior.

                                                                                                                                For more information on how to design recommendation cards for the best user experience, see Recommendation Row in the Android TV Design Spec.

                                                                                                                                Refreshing Recommendations

                                                                                                                                When refreshing recommendations, don't just remove and repost them, because doing so causes the recommendations to appear at the end of the recommendations row. Once a content item, such as a movie, has been played, remove it from the recommendations.

                                                                                                                                Customizing Recommendations

                                                                                                                                You can customize recommendation cards to convey branding information, by setting user interface elements such as the card's foreground and background image, color, app icon, title, and subtitle. To learn more, see Recommendation Row in the Android TV Design Spec.

                                                                                                                                Grouping Recommendations

                                                                                                                                You can optionally group recommendations based on recommendation source. For example, your app might provide two groups of recommendations: recommendations for content the user is subscribed to, and recommendations for new trending content the user might not be aware of.

                                                                                                                                The system ranks and orders recommendations for each group separately when creating or updating the recommendation row. By providing group information for your recommendations, you can ensure that your recommendations don’t get ordered below unrelated recommendations.

                                                                                                                                Use NotificationCompat.Builder.setGroup() to set the group key string of a recommendation. For example, to mark a recommendation as belonging to a group that contains new trending content, you might call setGroup("trending").

                                                                                                                                Create a Recommendations Service

                                                                                                                                Content recommendations are created with background processing. In order for your application to contribute to recommendations, create a service that periodically adds listings from your app's catalog to the system's list of recommendations.

                                                                                                                                The following code example illustrates how to extend IntentService to create a recommendation service for your application:

                                                                                                                                public class UpdateRecommendationsService extends IntentService {
                                                                                                                                    private static final String TAG = "UpdateRecommendationsService";
                                                                                                                                    private static final int MAX_RECOMMENDATIONS = 3;
                                                                                                                                
                                                                                                                                    public UpdateRecommendationsService() {
                                                                                                                                        super("RecommendationService");
                                                                                                                                    }
                                                                                                                                
                                                                                                                                    @Override
                                                                                                                                    protected void onHandleIntent(Intent intent) {
                                                                                                                                        Log.d(TAG, "Updating recommendation cards");
                                                                                                                                        HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList();
                                                                                                                                        if (recommendations == null) return;
                                                                                                                                
                                                                                                                                        int count = 0;
                                                                                                                                
                                                                                                                                        try {
                                                                                                                                            RecommendationBuilder builder = new RecommendationBuilder()
                                                                                                                                                    .setContext(getApplicationContext())
                                                                                                                                                    .setSmallIcon(R.drawable.videos_by_google_icon);
                                                                                                                                
                                                                                                                                            for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) {
                                                                                                                                                for (Movie movie : entry.getValue()) {
                                                                                                                                                    Log.d(TAG, "Recommendation - " + movie.getTitle());
                                                                                                                                
                                                                                                                                                    builder.setBackground(movie.getCardImageUrl())
                                                                                                                                                            .setId(count + 1)
                                                                                                                                                            .setPriority(MAX_RECOMMENDATIONS - count)
                                                                                                                                                            .setTitle(movie.getTitle())
                                                                                                                                                            .setDescription(getString(R.string.popular_header))
                                                                                                                                                            .setImage(movie.getCardImageUrl())
                                                                                                                                                            .setIntent(buildPendingIntent(movie))
                                                                                                                                                            .build();
                                                                                                                                
                                                                                                                                                    if (++count >= MAX_RECOMMENDATIONS) {
                                                                                                                                                        break;
                                                                                                                                                    }
                                                                                                                                                }
                                                                                                                                                if (++count >= MAX_RECOMMENDATIONS) {
                                                                                                                                                    break;
                                                                                                                                                }
                                                                                                                                            }
                                                                                                                                        } catch (IOException e) {
                                                                                                                                            Log.e(TAG, "Unable to update recommendation", e);
                                                                                                                                        }
                                                                                                                                    }
                                                                                                                                
                                                                                                                                    private PendingIntent buildPendingIntent(Movie movie) {
                                                                                                                                        Intent detailsIntent = new Intent(this, DetailsActivity.class);
                                                                                                                                        detailsIntent.putExtra("Movie", movie);
                                                                                                                                
                                                                                                                                        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
                                                                                                                                        stackBuilder.addParentStack(DetailsActivity.class);
                                                                                                                                        stackBuilder.addNextIntent(detailsIntent);
                                                                                                                                        // Ensure a unique PendingIntents, otherwise all
                                                                                                                                        // recommendations end up with the same PendingIntent
                                                                                                                                        detailsIntent.setAction(Long.toString(movie.getId()));
                                                                                                                                
                                                                                                                                        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
                                                                                                                                        return intent;
                                                                                                                                    }
                                                                                                                                }
                                                                                                                                

                                                                                                                                In order for this service to be recognized by the system and run, register it using your app manifest. The following code snippet illustrates how to declare this class as a service:

                                                                                                                                <manifest ... >
                                                                                                                                  <application ... >
                                                                                                                                    ...
                                                                                                                                
                                                                                                                                    <service
                                                                                                                                            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
                                                                                                                                            android:enabled="true" />
                                                                                                                                  </application>
                                                                                                                                </manifest>
                                                                                                                                

                                                                                                                                Build Recommendations

                                                                                                                                Once your recommendation service starts running, it must create recommendations and pass them to the Android framework. The framework receives the recommendations as Notification objects that use a specific template and are marked with a specific category.

                                                                                                                                Setting the Values

                                                                                                                                To set the UI element values for the recommendation card, you create a builder class that follows the builder pattern described as follows. First, you set the values of the recommendation card elements.

                                                                                                                                public class RecommendationBuilder {
                                                                                                                                    ...
                                                                                                                                
                                                                                                                                    public RecommendationBuilder setTitle(String title) {
                                                                                                                                            mTitle = title;
                                                                                                                                            return this;
                                                                                                                                        }
                                                                                                                                
                                                                                                                                        public RecommendationBuilder setDescription(String description) {
                                                                                                                                            mDescription = description;
                                                                                                                                            return this;
                                                                                                                                        }
                                                                                                                                
                                                                                                                                        public RecommendationBuilder setImage(String uri) {
                                                                                                                                            mImageUri = uri;
                                                                                                                                            return this;
                                                                                                                                        }
                                                                                                                                
                                                                                                                                        public RecommendationBuilder setBackground(String uri) {
                                                                                                                                            mBackgroundUri = uri;
                                                                                                                                            return this;
                                                                                                                                        }
                                                                                                                                ...
                                                                                                                                

                                                                                                                                Creating the Notification

                                                                                                                                Once you've set the values, you then build the notification, assigning the values from the builder class to the notification, and calling NotificationCompat.Builder.build().

                                                                                                                                Also, be sure to call setLocalOnly() so the NotificationCompat.BigPictureStyle notification won't show up on other devices.

                                                                                                                                The following code example demonstrates how to build a recommendation.

                                                                                                                                public class RecommendationBuilder {
                                                                                                                                    ...
                                                                                                                                
                                                                                                                                    public Notification build() throws IOException {
                                                                                                                                        ...
                                                                                                                                
                                                                                                                                        Notification notification = new NotificationCompat.BigPictureStyle(
                                                                                                                                                new NotificationCompat.Builder(mContext)
                                                                                                                                                        .setContentTitle(mTitle)
                                                                                                                                                        .setContentText(mDescription)
                                                                                                                                                        .setPriority(mPriority)
                                                                                                                                                        .setLocalOnly(true)
                                                                                                                                                        .setOngoing(true)
                                                                                                                                                        .setColor(mContext.getResources().getColor(R.color.fastlane_background))
                                                                                                                                                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
                                                                                                                                                        .setLargeIcon(image)
                                                                                                                                                        .setSmallIcon(mSmallIcon)
                                                                                                                                                        .setContentIntent(mIntent)
                                                                                                                                                        .setExtras(extras))
                                                                                                                                                .build();
                                                                                                                                
                                                                                                                                        return notification;
                                                                                                                                    }
                                                                                                                                }
                                                                                                                                

                                                                                                                                Run Recommendations Service

                                                                                                                                Your app's recommendation service must run periodically in order to create current recommendations. To run your service, create a class that runs a timer and invokes it at regular intervals. The following code example extends the BroadcastReceiver class to start periodic execution of a recommendation service every half hour:

                                                                                                                                public class BootupActivity extends BroadcastReceiver {
                                                                                                                                    private static final String TAG = "BootupActivity";
                                                                                                                                
                                                                                                                                    private static final long INITIAL_DELAY = 5000;
                                                                                                                                
                                                                                                                                    @Override
                                                                                                                                    public void onReceive(Context context, Intent intent) {
                                                                                                                                        Log.d(TAG, "BootupActivity initiated");
                                                                                                                                        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
                                                                                                                                            scheduleRecommendationUpdate(context);
                                                                                                                                        }
                                                                                                                                    }
                                                                                                                                
                                                                                                                                    private void scheduleRecommendationUpdate(Context context) {
                                                                                                                                        Log.d(TAG, "Scheduling recommendations update");
                                                                                                                                
                                                                                                                                        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
                                                                                                                                        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
                                                                                                                                        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);
                                                                                                                                
                                                                                                                                        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                                                                                                                                                INITIAL_DELAY,
                                                                                                                                                AlarmManager.INTERVAL_HALF_HOUR,
                                                                                                                                                alarmIntent);
                                                                                                                                    }
                                                                                                                                }
                                                                                                                                

                                                                                                                                This implementation of the BroadcastReceiver class must run after start up of the TV device where it is installed. To accomplish this, register this class in your app manifest with an intent filter that listens for the completion of the device boot process. The following sample code demonstrates how to add this configuration to the manifest:

                                                                                                                                <manifest ... >
                                                                                                                                  <application ... >
                                                                                                                                    <receiver android:name="com.example.android.tvleanback.BootupActivity"
                                                                                                                                              android:enabled="true"
                                                                                                                                              android:exported="false">
                                                                                                                                      <intent-filter>
                                                                                                                                        <action android:name="android.intent.action.BOOT_COMPLETED"/>
                                                                                                                                      </intent-filter>
                                                                                                                                    </receiver>
                                                                                                                                  </application>
                                                                                                                                </manifest>
                                                                                                                                

                                                                                                                                Important: Receiving a boot completed notification requires that your app requests the RECEIVE_BOOT_COMPLETED permission. For more information, see ACTION_BOOT_COMPLETED.

                                                                                                                                In your recommendation service class' onHandleIntent() method, post the recommendation to the manager as follows:

                                                                                                                                Notification notification = notificationBuilder.build();
                                                                                                                                mNotificationManager.notify(id, notification);
                                                                                                                                
                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                This class requires API level or higher

                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                Making TV Apps Searchable | Android Developers Skip to content

                                                                                                                                Most visited

                                                                                                                                Recently visited

                                                                                                                                navigation

                                                                                                                                  Making TV Apps Searchable

                                                                                                                                  Android TV uses the Android search interface to retrieve content data from installed apps and deliver search results to the user. Your app's content data can be included with these results, to give the user instant access to the content in your app.

                                                                                                                                  Your app must provide Android TV with the data fields from which it generates suggested search results as the user enters characters in the search dialog. To do that, your app must implement a Content Provider that serves up the suggestions along with a searchable.xml configuration file that describes the content provider and other vital information for Android TV. You also need an activity that handles the intent that fires when the user selects a suggested search result. All of this is described in more detail in Adding Custom Suggestions. Here are described the main points for Android TV apps.

                                                                                                                                  This lesson builds on your knowledge of using search in Android to show you how to make your app searchable in Android TV. Be sure you are familiar with the concepts explained in the Search API guide before following this lesson. See also the training Adding Search Functionality.

                                                                                                                                  This discussion describes some code from the Android Leanback sample app, available on GitHub.

                                                                                                                                  Identify Columns

                                                                                                                                  The SearchManager describes the data fields it expects by representing them as columns of an SQLite database. Regardless of your data's format, you must map your data fields to these columns, usually in the class that accessess your content data. For information about building a class that maps your existing data to the required fields, see Building a suggestion table.

                                                                                                                                  The SearchManager class includes several columns for Android TV. Some of the more important columns are described below.

                                                                                                                                  Value Description
                                                                                                                                  SUGGEST_COLUMN_TEXT_1 The name of your content (required)
                                                                                                                                  SUGGEST_COLUMN_TEXT_2 A text description of your content
                                                                                                                                  SUGGEST_COLUMN_RESULT_CARD_IMAGE An image/poster/cover for your content
                                                                                                                                  SUGGEST_COLUMN_CONTENT_TYPE The MIME type of your media (required)
                                                                                                                                  SUGGEST_COLUMN_VIDEO_WIDTH The resolution width of your media
                                                                                                                                  SUGGEST_COLUMN_VIDEO_HEIGHT The resolution height of your media
                                                                                                                                  SUGGEST_COLUMN_PRODUCTION_YEAR The production year of your content (required)
                                                                                                                                  SUGGEST_COLUMN_DURATION The duration in milliseconds of your media (required)

                                                                                                                                  The search framework requires the following columns:

                                                                                                                                  When the values of these columns for your content match the values for the same content from other providers found by Google servers, the system provides a deep link to your app in the details view for the content, along with links to the apps of other providers. This is discussed more in Display Content in the Details Screen, below.

                                                                                                                                  Your application's database class might define the columns as follows:

                                                                                                                                  public class VideoDatabase {
                                                                                                                                    //The columns we'll include in the video database table
                                                                                                                                    public static final String KEY_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1;
                                                                                                                                    public static final String KEY_DESCRIPTION = SearchManager.SUGGEST_COLUMN_TEXT_2;
                                                                                                                                    public static final String KEY_ICON = SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE;
                                                                                                                                    public static final String KEY_DATA_TYPE = SearchManager.SUGGEST_COLUMN_CONTENT_TYPE;
                                                                                                                                    public static final String KEY_IS_LIVE = SearchManager.SUGGEST_COLUMN_IS_LIVE;
                                                                                                                                    public static final String KEY_VIDEO_WIDTH = SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH;
                                                                                                                                    public static final String KEY_VIDEO_HEIGHT = SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT;
                                                                                                                                    public static final String KEY_AUDIO_CHANNEL_CONFIG =
                                                                                                                                            SearchManager.SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG;
                                                                                                                                    public static final String KEY_PURCHASE_PRICE = SearchManager.SUGGEST_COLUMN_PURCHASE_PRICE;
                                                                                                                                    public static final String KEY_RENTAL_PRICE = SearchManager.SUGGEST_COLUMN_RENTAL_PRICE;
                                                                                                                                    public static final String KEY_RATING_STYLE = SearchManager.SUGGEST_COLUMN_RATING_STYLE;
                                                                                                                                    public static final String KEY_RATING_SCORE = SearchManager.SUGGEST_COLUMN_RATING_SCORE;
                                                                                                                                    public static final String KEY_PRODUCTION_YEAR = SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR;
                                                                                                                                    public static final String KEY_COLUMN_DURATION = SearchManager.SUGGEST_COLUMN_DURATION;
                                                                                                                                    public static final String KEY_ACTION = SearchManager.SUGGEST_COLUMN_INTENT_ACTION;
                                                                                                                                  ...
                                                                                                                                  

                                                                                                                                  When you build the map from the SearchManager columns to your data fields, you must also specify the _ID to give each row a unique ID.

                                                                                                                                  ...
                                                                                                                                    private static HashMap buildColumnMap() {
                                                                                                                                      HashMap map = new HashMap();
                                                                                                                                      map.put(KEY_NAME, KEY_NAME);
                                                                                                                                      map.put(KEY_DESCRIPTION, KEY_DESCRIPTION);
                                                                                                                                      map.put(KEY_ICON, KEY_ICON);
                                                                                                                                      map.put(KEY_DATA_TYPE, KEY_DATA_TYPE);
                                                                                                                                      map.put(KEY_IS_LIVE, KEY_IS_LIVE);
                                                                                                                                      map.put(KEY_VIDEO_WIDTH, KEY_VIDEO_WIDTH);
                                                                                                                                      map.put(KEY_VIDEO_HEIGHT, KEY_VIDEO_HEIGHT);
                                                                                                                                      map.put(KEY_AUDIO_CHANNEL_CONFIG, KEY_AUDIO_CHANNEL_CONFIG);
                                                                                                                                      map.put(KEY_PURCHASE_PRICE, KEY_PURCHASE_PRICE);
                                                                                                                                      map.put(KEY_RENTAL_PRICE, KEY_RENTAL_PRICE);
                                                                                                                                      map.put(KEY_RATING_STYLE, KEY_RATING_STYLE);
                                                                                                                                      map.put(KEY_RATING_SCORE, KEY_RATING_SCORE);
                                                                                                                                      map.put(KEY_PRODUCTION_YEAR, KEY_PRODUCTION_YEAR);
                                                                                                                                      map.put(KEY_COLUMN_DURATION, KEY_COLUMN_DURATION);
                                                                                                                                      map.put(KEY_ACTION, KEY_ACTION);
                                                                                                                                      map.put(BaseColumns._ID, "rowid AS " +
                                                                                                                                              BaseColumns._ID);
                                                                                                                                      map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " +
                                                                                                                                              SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
                                                                                                                                      map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " +
                                                                                                                                              SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
                                                                                                                                      return map;
                                                                                                                                    }
                                                                                                                                  ...
                                                                                                                                  

                                                                                                                                  In the example above, notice the mapping to the SUGGEST_COLUMN_INTENT_DATA_ID field. This is the portion of the URI that points to the content unique to the data in this row — that is, the last part of the URI describing where the content is stored. The first part of the URI, when it is common to all of the rows in the table, is set in the searchable.xml file as the android:searchSuggestIntentData attribute, as described in Handle Search Suggestions, below.

                                                                                                                                  If the first part of the URI is different for each row in the table, you map that value with the SUGGEST_COLUMN_INTENT_DATA field. When the user selects this content, the intent that fires provides the intent data from the combination of the SUGGEST_COLUMN_INTENT_DATA_ID and either the android:searchSuggestIntentData attribute or the SUGGEST_COLUMN_INTENT_DATA field value.

                                                                                                                                  Provide Search Suggestion Data

                                                                                                                                  Implement a Content Provider to return search term suggestions to the Android TV search dialog. The system queries your content provider for suggestions by calling the query() method each time a letter is typed. In your implementation of query(), your content provider searches your suggestion data and returns a Cursor that points to the rows you have designated for suggestions.

                                                                                                                                  @Override
                                                                                                                                    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                                                                                                                                                        String sortOrder) {
                                                                                                                                      // Use the UriMatcher to see what kind of query we have and format the db query accordingly
                                                                                                                                      switch (URI_MATCHER.match(uri)) {
                                                                                                                                        case SEARCH_SUGGEST:
                                                                                                                                            Log.d(TAG, "search suggest: " + selectionArgs[0] + " URI: " + uri);
                                                                                                                                            if (selectionArgs == null) {
                                                                                                                                                throw new IllegalArgumentException(
                                                                                                                                                        "selectionArgs must be provided for the Uri: " + uri);
                                                                                                                                            }
                                                                                                                                            return getSuggestions(selectionArgs[0]);
                                                                                                                                        default:
                                                                                                                                            throw new IllegalArgumentException("Unknown Uri: " + uri);
                                                                                                                                      }
                                                                                                                                    }
                                                                                                                                  
                                                                                                                                    private Cursor getSuggestions(String query) {
                                                                                                                                      query = query.toLowerCase();
                                                                                                                                      String[] columns = new String[]{
                                                                                                                                        BaseColumns._ID,
                                                                                                                                        VideoDatabase.KEY_NAME,
                                                                                                                                        VideoDatabase.KEY_DESCRIPTION,
                                                                                                                                        VideoDatabase.KEY_ICON,
                                                                                                                                        VideoDatabase.KEY_DATA_TYPE,
                                                                                                                                        VideoDatabase.KEY_IS_LIVE,
                                                                                                                                        VideoDatabase.KEY_VIDEO_WIDTH,
                                                                                                                                        VideoDatabase.KEY_VIDEO_HEIGHT,
                                                                                                                                        VideoDatabase.KEY_AUDIO_CHANNEL_CONFIG,
                                                                                                                                        VideoDatabase.KEY_PURCHASE_PRICE,
                                                                                                                                        VideoDatabase.KEY_RENTAL_PRICE,
                                                                                                                                        VideoDatabase.KEY_RATING_STYLE,
                                                                                                                                        VideoDatabase.KEY_RATING_SCORE,
                                                                                                                                        VideoDatabase.KEY_PRODUCTION_YEAR,
                                                                                                                                        VideoDatabase.KEY_COLUMN_DURATION,
                                                                                                                                        VideoDatabase.KEY_ACTION,
                                                                                                                                        SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
                                                                                                                                      };
                                                                                                                                      return mVideoDatabase.getWordMatch(query, columns);
                                                                                                                                    }
                                                                                                                                  ...
                                                                                                                                  

                                                                                                                                  In your manifest file, the content provider receives special treatment. Rather than getting tagged as an activity, it is described as a <provider>. The provider includes the android:searchSuggestAuthority attribute to tell the system the namespace of your content provider. Also, you must set its android:exported attribute to "true" so that the Android global search can use the results returned from it.

                                                                                                                                  <provider android:name="com.example.android.tvleanback.VideoContentProvider"
                                                                                                                                      android:authorities="com.example.android.tvleanback"
                                                                                                                                      android:exported="true" />
                                                                                                                                  

                                                                                                                                  Handle Search Suggestions

                                                                                                                                  Your app must include a res/xml/searchable.xml file to configure the search suggestions settings. It inlcudes the android:searchSuggestAuthority attribute to tell the system the namespace of your content provider. This must match the string value you specify in the android:authorities attribute of the <provider> element in your AndroidManifest.xml file.

                                                                                                                                  The searchable.xml file must also include the android:searchSuggestIntentAction with the value "android.intent.action.VIEW" to define the intent action for providing a custom suggestion. This is different from the intent action for providing a search term, explained below. See also, Declaring the intent action for other ways to declare the intent action for suggestions.

                                                                                                                                  Along with the intent action, your app must provide the intent data, which you specify with the android:searchSuggestIntentData attribute. This is the first part of the URI that points to the content. It describes the portion of the URI common to all rows in the mapping table for that content. The portion of the URI that is unique to each row is established with the SUGGEST_COLUMN_INTENT_DATA_ID field, as described above in Identify Columns. See also, Declaring the intent data for other ways to declare the intent data for suggestions.

                                                                                                                                  Also, note the android:searchSuggestSelection=" ?" attribute which specifies the value passed as the selection parameter of the query() method where the question mark (?) value is replaced with the query text.

                                                                                                                                  Finally, you must also include the android:includeInGlobalSearch attribute with the value "true". Here is an example searchable.xml file:

                                                                                                                                  <searchable xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                      android:label="@string/search_label"
                                                                                                                                          android:hint="@string/search_hint"
                                                                                                                                          android:searchSettingsDescription="@string/settings_description"
                                                                                                                                          android:searchSuggestAuthority="com.example.android.tvleanback"
                                                                                                                                          android:searchSuggestIntentAction="android.intent.action.VIEW"
                                                                                                                                          android:searchSuggestIntentData="content://com.example.android.tvleanback/video_database_leanback"
                                                                                                                                          android:searchSuggestSelection=" ?"
                                                                                                                                          android:searchSuggestThreshold="1"
                                                                                                                                          android:includeInGlobalSearch="true"
                                                                                                                                      >
                                                                                                                                  </searchable>
                                                                                                                                  

                                                                                                                                  Handle Search Terms

                                                                                                                                  As soon as the search dialog has a word which matches the value in one of your app's columns (described in Identifying Columns, above), the system fires the ACTION_SEARCH intent. The activity in your app which handles that intent searches the repository for columns with the given word in their values, and returns a list of content items with those columns. In your AndroidManifest.xml file, you designate the activity which handles the ACTION_SEARCH intent like this:

                                                                                                                                  ...
                                                                                                                                    <activity
                                                                                                                                        android:name="com.example.android.tvleanback.DetailsActivity"
                                                                                                                                        android:exported="true">
                                                                                                                                  
                                                                                                                                        <!-- Receives the search request. -->
                                                                                                                                        <intent-filter>
                                                                                                                                            <action android:name="android.intent.action.SEARCH" />
                                                                                                                                            <!-- No category needed, because the Intent will specify this class component -->
                                                                                                                                        </intent-filter>
                                                                                                                                  
                                                                                                                                        <!-- Points to searchable meta data. -->
                                                                                                                                        <meta-data android:name="android.app.searchable"
                                                                                                                                            android:resource="@xml/searchable" />
                                                                                                                                    </activity>
                                                                                                                                  ...
                                                                                                                                    <!-- Provides search suggestions for keywords against video meta data. -->
                                                                                                                                    <provider android:name="com.example.android.tvleanback.VideoContentProvider"
                                                                                                                                        android:authorities="com.example.android.tvleanback"
                                                                                                                                        android:exported="true" />
                                                                                                                                  ...
                                                                                                                                  

                                                                                                                                  The activity must also describe the searchable configuration with a reference to the searchable.xml file. To use the global search dialog, the manifest must describe which activity should receive search queries. The manifest must also describe the <provider> element, exactly as it is described in the searchable.xml file.

                                                                                                                                  Deep Link to Your App in the Details Screen

                                                                                                                                  If you have set up the search configuration as described in Handle Search Suggestions and mapped the SUGGEST_COLUMN_TEXT_1, SUGGEST_COLUMN_CONTENT_TYPE, and SUGGEST_COLUMN_PRODUCTION_YEAR fields as described in Identify Columns, a deep link to a watch action for your content appears in the details screen that launches when the user selects a search result, as shown in figure 1.

                                                                                                                                  Deep link in the details screen

                                                                                                                                  Figure 1. The details screen displays a deep link for the Videos by Google (Leanback) sample app. Sintel: © copyright Blender Foundation, www.sintel.org.

                                                                                                                                  When the user selects the link for your app, identified by the "Available On" button in the details screen, the system launches the activity which handles the ACTION_VIEW (set as android:searchSuggestIntentAction with the value "android.intent.action.VIEW" in the searchable.xml file).

                                                                                                                                  You can also set up a custom intent to launch your activity, and this is demonstrated in the Android Leanback sample app. Note that the sample app launches its own LeanbackDetailsFragment to show the details for the selected media, but you should launch the activity that plays the media immediately to save the user another click or two.

                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                  This class requires API level or higher

                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                  Searching within TV Apps | Android Developers Skip to content

                                                                                                                                  Most visited

                                                                                                                                  Recently visited

                                                                                                                                  navigation

                                                                                                                                    Searching within TV Apps

                                                                                                                                    Users frequently have specific content in mind when using a media app on TV. If your app contains a large catalog of content, browsing for a specific title may not be the most efficient way for users to find what they are looking for. A search interface can help your users get to the content they want faster than browsing.

                                                                                                                                    The Leanback support library provides a set of classes to enable a standard search interface within your app that is consistent with other search functions on TV and provides features such as voice input.

                                                                                                                                    This lesson discusses how to provide a search interface in your app using Leanback support library classes.

                                                                                                                                    Add a Search Action

                                                                                                                                    When you use the BrowseFragment class for a media browsing interface, you can enable a search interface as a standard part of the user interface. The search interface is an icon that appears in the layout when you set View.OnClickListener on the BrowseFragment object. The following sample code demonstrates this technique.

                                                                                                                                    @Override
                                                                                                                                    public void onCreate(Bundle savedInstanceState) {
                                                                                                                                        super.onCreate(savedInstanceState);
                                                                                                                                        setContentView(R.layout.browse_activity);
                                                                                                                                    
                                                                                                                                        mBrowseFragment = (BrowseFragment)
                                                                                                                                                getFragmentManager().findFragmentById(R.id.browse_fragment);
                                                                                                                                    
                                                                                                                                        ...
                                                                                                                                    
                                                                                                                                        mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
                                                                                                                                            @Override
                                                                                                                                            public void onClick(View view) {
                                                                                                                                                Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
                                                                                                                                                startActivity(intent);
                                                                                                                                            }
                                                                                                                                        });
                                                                                                                                    
                                                                                                                                        mBrowseFragment.setAdapter(buildAdapter());
                                                                                                                                    }
                                                                                                                                    

                                                                                                                                    Note: You can set the color of the search icon using the setSearchAffordanceColor(int).

                                                                                                                                    Add a Search Input and Results

                                                                                                                                    When a user selects the search icon, the system invokes a search activity via the defined intent. Your search activity should use a linear layout containing a SearchFragment. This fragment must also implement the SearchFragment.SearchResultProvider interface in order to display the results of a search.

                                                                                                                                    The following code sample shows how to extend the SearchFragment class to provide a search interface and results:

                                                                                                                                    public class MySearchFragment extends SearchFragment
                                                                                                                                            implements SearchFragment.SearchResultProvider {
                                                                                                                                    
                                                                                                                                        private static final int SEARCH_DELAY_MS = 300;
                                                                                                                                        private ArrayObjectAdapter mRowsAdapter;
                                                                                                                                        private Handler mHandler = new Handler();
                                                                                                                                        private SearchRunnable mDelayedLoad;
                                                                                                                                    
                                                                                                                                        @Override
                                                                                                                                        public void onCreate(Bundle savedInstanceState) {
                                                                                                                                            super.onCreate(savedInstanceState);
                                                                                                                                    
                                                                                                                                            mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
                                                                                                                                            setSearchResultProvider(this);
                                                                                                                                            setOnItemClickedListener(getDefaultItemClickedListener());
                                                                                                                                            mDelayedLoad = new SearchRunnable();
                                                                                                                                        }
                                                                                                                                    
                                                                                                                                        @Override
                                                                                                                                        public ObjectAdapter getResultsAdapter() {
                                                                                                                                            return mRowsAdapter;
                                                                                                                                        }
                                                                                                                                    
                                                                                                                                        @Override
                                                                                                                                        public boolean onQueryTextChange(String newQuery) {
                                                                                                                                            mRowsAdapter.clear();
                                                                                                                                            if (!TextUtils.isEmpty(newQuery)) {
                                                                                                                                                mDelayedLoad.setSearchQuery(newQuery);
                                                                                                                                                mHandler.removeCallbacks(mDelayedLoad);
                                                                                                                                                mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
                                                                                                                                            }
                                                                                                                                            return true;
                                                                                                                                        }
                                                                                                                                    
                                                                                                                                        @Override
                                                                                                                                        public boolean onQueryTextSubmit(String query) {
                                                                                                                                            mRowsAdapter.clear();
                                                                                                                                            if (!TextUtils.isEmpty(query)) {
                                                                                                                                                mDelayedLoad.setSearchQuery(query);
                                                                                                                                                mHandler.removeCallbacks(mDelayedLoad);
                                                                                                                                                mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
                                                                                                                                            }
                                                                                                                                            return true;
                                                                                                                                        }
                                                                                                                                    }
                                                                                                                                    

                                                                                                                                    The example code shown above is meant to be used with a separate SearchRunnable class that runs the search query on a separate thread. This technique keeps potentially slow-running queries from blocking the main user interface thread.

                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                    This class requires API level or higher

                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                    Building TV Channels | Android Developers Skip to content

                                                                                                                                    Most visited

                                                                                                                                    Recently visited

                                                                                                                                    navigation

                                                                                                                                      Building TV Channels

                                                                                                                                      Dependencies and Prerequisites

                                                                                                                                      • Android 5.0 (API level 21) or higher

                                                                                                                                      You should also read

                                                                                                                                      Try It Out

                                                                                                                                      Watching live TV shows and other continuous, channel-based content is a big part of the TV experience. Users are accustomed to selecting and watching shows on TV by channel browsing. To provide your users a similar experience, use the TV Input Framework to create channels for publishing video or music content so that your media appears alongside traditional TV channels in the programming guide.

                                                                                                                                      Android supports receiving and playback of live video content through the TV Input Framework in Android 5.0 (API level 21). This framework provides a unified method for receiving audio and video channel content from hardware sources, such as HDMI ports and built-in-tuners, and software sources, such as video streamed over the internet.

                                                                                                                                      The framework enables developers to define live TV input sources by implementing a TV input service. This service publishes a list of channels and programs to the TV Provider. The live TV app on a TV device gets the list of available channels and programs from the TV Provider and displays them to a user. When a user selects a specific channel, the live TV app creates a session for the associated TV input service through the TV Input Manager, and tells the TV input service to tune to the requested channel and play the content to a display surface provided by the TV app.

                                                                                                                                      Figure 1. Functional diagram of the TV Input Framework

                                                                                                                                      The TV Input Framework is designed to provide access to a wide variety of live TV input sources and bring them together in a single user interface for users to browse, view, and enjoy content. Building a TV input service for your content can help make it more accessible on TV devices.

                                                                                                                                      Topics

                                                                                                                                      Developing a TV Input Service
                                                                                                                                      Learn how to develop a TV input service, which works with the system TV app.
                                                                                                                                      Working with Channel Data
                                                                                                                                      Learn how to describe channel and program data for the system.
                                                                                                                                      Managing User Interaction
                                                                                                                                      Learn how to present overlays, manage content availability, and handle content selection.
                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                      This class requires API level or higher

                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                      Developing a TV Input Service | Android Developers Skip to content

                                                                                                                                      Most visited

                                                                                                                                      Recently visited

                                                                                                                                      navigation

                                                                                                                                        Developing a TV Input Service

                                                                                                                                        A TV input service represents a media stream source, and lets you present your media content in a linear, broadcast TV fashion as channels and programs. With the TV input service, you can provide parental controls, program guide information, and content ratings. The TV input service works with the Android system TV app, developed for the device and immutable by third-party apps, which ultimately controls and presents content on the TV. See TV Input Framework for more information about the framework architecture and its components.

                                                                                                                                        To develop a TV input service, you implement the following components:

                                                                                                                                        • TvInputService provides long-running and background availability for the TV input
                                                                                                                                        • TvInputService.Session maintains the TV input state and communicates with the hosting app
                                                                                                                                        • TvContract describes the channels and programs available to the TV input
                                                                                                                                        • TvContract.Channels represents information about a TV channel
                                                                                                                                        • TvContract.Programs describes a TV program with data such as program title and start time
                                                                                                                                        • TvTrackInfo represents an audio, video, or subtitle track
                                                                                                                                        • TvContentRating describes a content rating, allows for custom content rating schemes
                                                                                                                                        • TvInputManager provides an API to the system TV app and manages the interaction with TV inputs and apps

                                                                                                                                        Declare Your TV Input Service in the Manifest

                                                                                                                                        Your app manifest must declare your TvInputService. Within that declaration, specify the BIND_TV_INPUT permission to allow the service to connect the TV input to the system. A system service (TvInputManagerService) performs the binding and has that permission. The system TV app sends requests to TV input services via the TvInputManager interface. The service declaration must also include an intent filter that specifies the TvInputService as the action to perform with the intent. Also within the service declaration, declare the service meta data in a separate XML resource. The service declaration, the intent filter and the service meta data are described in the following example.

                                                                                                                                        <service android:name="com.example.sampletvinput.SampleTvInput"
                                                                                                                                            android:label="@string/sample_tv_input_label"
                                                                                                                                            android:permission="android.permission.BIND_TV_INPUT">
                                                                                                                                            <intent-filter>
                                                                                                                                                <action android:name="android.media.tv.TvInputService" />
                                                                                                                                            </intent-filter>
                                                                                                                                            <meta-data android:name="android.media.tv.input"
                                                                                                                                              android:resource="@xml/sample_tv_input" />
                                                                                                                                        </service>
                                                                                                                                        

                                                                                                                                        Define the service meta data in separate XML file, as shown in the following example. The service meta data must include a setup interface that describes the TV input's initial configuration and channel scan. The service meta data file is located in the XML resources directory for your application and must match the name of the resource in the manifest. Using the example manifest entries above, you would create an XML file in the location res/xml/sample_tv_input.xml, with the following contents:

                                                                                                                                        <tv-input xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                          android:setupActivity="com.example.sampletvinput.SampleTvInputSetupActivity" />
                                                                                                                                        

                                                                                                                                        Define Your TV Input Service

                                                                                                                                        Figure 1.TvInputService lifecycle.

                                                                                                                                        For your service, you extend the TvInputService class. A TvInputService implementation is a bound service where the system service (TvInputManagerService) is the client that binds to it. The service life cycle methods you need to implement are illustrated in figure 1.

                                                                                                                                        The onCreate() method initializes and starts the HandlerThread which provides a process thread separate from the UI thread to handle system-driven actions. In the following example, the onCreate() method initializes the CaptioningManager and prepares to handle the ACTION_BLOCKED_RATINGS_CHANGED and ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED actions. These actions describe system intents fired when the user changes the parental control settings, and when there is a change on the list of blocked ratings.

                                                                                                                                        @Override
                                                                                                                                        public void onCreate() {
                                                                                                                                            super.onCreate();
                                                                                                                                            mHandlerThread = new HandlerThread(getClass()
                                                                                                                                              .getSimpleName());
                                                                                                                                            mHandlerThread.start();
                                                                                                                                            mDbHandler = new Handler(mHandlerThread.getLooper());
                                                                                                                                            mHandler = new Handler();
                                                                                                                                            mCaptioningManager = (CaptioningManager)
                                                                                                                                              getSystemService(Context.CAPTIONING_SERVICE);
                                                                                                                                        
                                                                                                                                            setTheme(android.R.style.Theme_Holo_Light_NoActionBar);
                                                                                                                                        
                                                                                                                                            mSessions = new ArrayList<BaseTvInputSessionImpl>();
                                                                                                                                            IntentFilter intentFilter = new IntentFilter();
                                                                                                                                            intentFilter.addAction(TvInputManager
                                                                                                                                              .ACTION_BLOCKED_RATINGS_CHANGED);
                                                                                                                                            intentFilter.addAction(TvInputManager
                                                                                                                                              .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
                                                                                                                                            registerReceiver(mBroadcastReceiver, intentFilter);
                                                                                                                                        }
                                                                                                                                        

                                                                                                                                        See Control Content for more information about working with blocked content and providing parental control. See TvInputManager for more system-driven actions that you may want to handle in your TV input service.

                                                                                                                                        The TvInputService creates a TvInputService.Session that implements Handler.Callback to handle player state changes. With onSetSurface(), the TvInputService.Session sets the Surface with the video content. See Integrate Player with Surface for more information about working with Surface to render video.

                                                                                                                                        The TvInputService.Session handles the onTune() event when the user selects a channel, and notifies the system TV app for changes in the content and content meta data. These notify() methods are described in Control Content and Handle Track Selection further in this training.

                                                                                                                                        Define Your Setup Activity

                                                                                                                                        The system TV app works with the setup activity you define for your TV input. The setup activity is required and must provide at least one channel record for the system database. The system TV app will invoke the setup activity when it cannot find a channel for the TV input.

                                                                                                                                        The setup activity describes to the system TV app the channels made available through the TV input, as demonstrated in the next lesson, Creating and Updating Channel Data.

                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                        This class requires API level or higher

                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                        Working with Channel Data | Android Developers Skip to content

                                                                                                                                        Most visited

                                                                                                                                        Recently visited

                                                                                                                                        navigation

                                                                                                                                          Working with Channel Data

                                                                                                                                          Your TV input must provide Electronic Program Guide (EPG) data for at least one channel in its setup activity. You should also periodically update that data, with consideration for the size of the update and the processing thread that handles it. This lesson discusses creating and updating channel and program data on the system database with these considerations in mind.

                                                                                                                                           

                                                                                                                                          Get Permission

                                                                                                                                          In order for your TV input to work with EPG data, it must declare the read and write permissions in its Android manifest file as follows:

                                                                                                                                          <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
                                                                                                                                          <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
                                                                                                                                          

                                                                                                                                          Register Channels in the Database

                                                                                                                                          The Android TV system database maintains records of channel data for TV inputs. In your setup activity, for each of your channels, you must map your channel data to the following fields of the TvContract.Channels class:

                                                                                                                                          Although the TV input framework is generic enough to handle both traditional broadcast and over-the-top (OTT) content without any distinction, you may want to define the following columns in addition to those above to better identify traditional broadcast channels:

                                                                                                                                          For internet streaming based TV inputs, assign your own values to the above accordingly so that each channel can be identified uniquely.

                                                                                                                                          Pull your channel metadata (in XML, JSON, or whatever) from your backend server, and in your setup activity map the values to the system database as follows:

                                                                                                                                          ContentValues values = new ContentValues();
                                                                                                                                          
                                                                                                                                          values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.mNumber);
                                                                                                                                          values.put(Channels.COLUMN_DISPLAY_NAME, channel.mName);
                                                                                                                                          values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.mOriginalNetworkId);
                                                                                                                                          values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.mTransportStreamId);
                                                                                                                                          values.put(Channels.COLUMN_SERVICE_ID, channel.mServiceId);
                                                                                                                                          values.put(Channels.COLUMN_VIDEO_FORMAT, channel.mVideoFormat);
                                                                                                                                          
                                                                                                                                          Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
                                                                                                                                          

                                                                                                                                          In the example above, channel is an object which holds channel metadata from the backend server.

                                                                                                                                          Present Channel and Program Information

                                                                                                                                          The system TV app presents channel and program information to users as they flip through channels, as shown in figure 1. To make sure the channel and program information works with the system TV app's channel and program information presenter, follow the guidelines below.

                                                                                                                                          1. Channel number (COLUMN_DISPLAY_NUMBER)
                                                                                                                                          2. Icon (android:icon in the TV input's manifest)
                                                                                                                                          3. Program description (COLUMN_SHORT_DESCRIPTION)
                                                                                                                                          4. Program title (COLUMN_TITLE)
                                                                                                                                          5. Channel logo (TvContract.Channels.Logo)
                                                                                                                                            • Use the color #EEEEEE to match the surrounding text
                                                                                                                                            • Don't include padding
                                                                                                                                          6. Poster art (COLUMN_POSTER_ART_URI)
                                                                                                                                            • Aspect ratio between 16:9 and 4:3

                                                                                                                                          Figure 1. The system TV app channel and program information presenter.

                                                                                                                                          The system TV app provides the same information through the program guide, including poster art, as shown in figure 2.

                                                                                                                                          Figure 2. The system TV app program guide.

                                                                                                                                          Update Channel Data

                                                                                                                                          When updating existing channel data, use the update() method instead of deleting and re-adding the data. You can identify the current version of the data by using Channels.COLUMN_VERSION_NUMBER and Programs.COLUMN_VERSION_NUMBER when choosing the records to update.

                                                                                                                                          Note: Adding channel data to the ContentProvider can take time. Only add current programs (those within two hours of the current time) when you update, and use a Sync Adapter to update the rest of the channel data in the background. See the Android TV Live TV Sample App for an example.

                                                                                                                                          Batch Loading Channel Data

                                                                                                                                          When updating the system database with a large amount of channel data, use the ContentResolver applyBatch() or bulkInsert() method. Here's an example using applyBatch():

                                                                                                                                          ArrayList<ContentProviderOperation> ops = new ArrayList<>();
                                                                                                                                          int programsCount = mChannelInfo.mPrograms.size();
                                                                                                                                          for (int j = 0; j < programsCount; ++j) {
                                                                                                                                              ProgramInfo program = mChannelInfo.mPrograms.get(j);
                                                                                                                                              ops.add(ContentProviderOperation.newInsert(
                                                                                                                                                      TvContract.Programs.CONTENT_URI)
                                                                                                                                                      .withValues(programs.get(j))
                                                                                                                                                      .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS,
                                                                                                                                                              programStartSec * 1000)
                                                                                                                                                      .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS,
                                                                                                                                                              (programStartSec + program.mDurationSec) * 1000)
                                                                                                                                                      .build());
                                                                                                                                              programStartSec = programStartSec + program.mDurationSec;
                                                                                                                                              if (j % 100 == 99 || j == programsCount - 1) {
                                                                                                                                                  try {
                                                                                                                                                      getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
                                                                                                                                                  } catch (RemoteException | OperationApplicationException e) {
                                                                                                                                                      Log.e(TAG, "Failed to insert programs.", e);
                                                                                                                                                      return;
                                                                                                                                                  }
                                                                                                                                                  ops.clear();
                                                                                                                                              }
                                                                                                                                          }
                                                                                                                                          

                                                                                                                                          Processing Channel Data Asynchronously

                                                                                                                                          Data manipulation, such as fetching a stream from the server or accessing the database, should not block the UI thread. Using an AsyncTask is one way to perform updates asynchronously. For example, when loading channel info from a backend server, you can use AsyncTask as follows:

                                                                                                                                          private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void>> {
                                                                                                                                          
                                                                                                                                              private Context mContext;
                                                                                                                                          
                                                                                                                                              public LoadTvInputTask(Context context) {
                                                                                                                                                  mContext = context;
                                                                                                                                              }
                                                                                                                                          
                                                                                                                                              @Override
                                                                                                                                              protected Void doInBackground(Uri... uris) {
                                                                                                                                                  try {
                                                                                                                                                      fetchUri(uris[0]);
                                                                                                                                                  } catch (IOException e) {
                                                                                                                                                    Log.d(“LoadTvInputTask”, “fetchUri error”);
                                                                                                                                                  }
                                                                                                                                                  return null;
                                                                                                                                              }
                                                                                                                                          
                                                                                                                                              private void fetchUri(Uri videoUri) throws IOException {
                                                                                                                                                  InputStream inputStream = null;
                                                                                                                                                  try {
                                                                                                                                                      inputStream = mContext.getContentResolver().openInputStream(videoUri);
                                                                                                                                                      XmlPullParser parser = Xml.newPullParser();
                                                                                                                                                      try {
                                                                                                                                                          parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
                                                                                                                                                          parser.setInput(inputStream, null);
                                                                                                                                                          sTvInput = ChannelXMLParser.parseTvInput(parser);
                                                                                                                                                          sSampleChannels = ChannelXMLParser.parseChannelXML(parser);
                                                                                                                                                      } catch (XmlPullParserException e) {
                                                                                                                                                          e.printStackTrace();
                                                                                                                                                      }
                                                                                                                                                  } finally {
                                                                                                                                                      if (inputStream != null) {
                                                                                                                                                          inputStream.close();
                                                                                                                                                      }
                                                                                                                                                  }
                                                                                                                                              }
                                                                                                                                          }
                                                                                                                                          

                                                                                                                                          If you need to update EPG data on a regular basis, consider using a Sync Adapter or JobScheduler to run the update process during idle time, such as every day at 3:00 a.m. See the Android TV live TV sample app for an example.

                                                                                                                                          Other techniques to separate the data update tasks from the UI thread include using the HandlerThread class, or you may implement your own using Looper and Handler classes. See Processes and Threads for more information.

                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                          This class requires API level or higher

                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                          Managing User Interaction | Android Developers Skip to content

                                                                                                                                          Most visited

                                                                                                                                          Recently visited

                                                                                                                                          navigation

                                                                                                                                            Managing User Interaction

                                                                                                                                            In the live TV experience the user changes channels and is presented with channel and program information briefly before the information disappears. Other types of information, such as messages ("DO NOT ATTEMPT AT HOME"), subtitles, or ads may need to persist. As with any TV app, such information should not interfere with the program content playing on the screen.

                                                                                                                                            Figure 1. An overlay message in a live TV app.

                                                                                                                                            Also consider whether certain program content should be presented, given the content's rating and parental control settings, and how your app behaves and informs the user when content is blocked or unavailable. This lesson describes how to develop your TV input's user experience for these considerations.

                                                                                                                                            Integrate Player with Surface

                                                                                                                                            Your TV input must render video onto a Surface object, which is passed by the TvInputService.Session.onSetSurface() method. Here's an example of how to use a MediaPlayer instance for playing content in the Surface object:

                                                                                                                                            @Override
                                                                                                                                            public boolean onSetSurface(Surface surface) {
                                                                                                                                                if (mPlayer != null) {
                                                                                                                                                    mPlayer.setSurface(surface);
                                                                                                                                                }
                                                                                                                                                mSurface = surface;
                                                                                                                                                return true;
                                                                                                                                            }
                                                                                                                                            
                                                                                                                                            @Override
                                                                                                                                            public void onSetStreamVolume(float volume) {
                                                                                                                                                if (mPlayer != null) {
                                                                                                                                                    mPlayer.setVolume(volume, volume);
                                                                                                                                                }
                                                                                                                                                mVolume = volume;
                                                                                                                                            }
                                                                                                                                            

                                                                                                                                            Similarly, here's how to do it using ExoPlayer:

                                                                                                                                            @Override
                                                                                                                                            public boolean onSetSurface(Surface surface) {
                                                                                                                                                if (mPlayer != null) {
                                                                                                                                                    mPlayer.sendMessage(mVideoRenderer,
                                                                                                                                                            MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,
                                                                                                                                                            surface);
                                                                                                                                                }
                                                                                                                                                mSurface = surface;
                                                                                                                                                return true;
                                                                                                                                            }
                                                                                                                                            
                                                                                                                                            @Override
                                                                                                                                            public void onSetStreamVolume(float volume) {
                                                                                                                                                if (mPlayer != null) {
                                                                                                                                                    mPlayer.sendMessage(mAudioRenderer,
                                                                                                                                                            MediaCodecAudioTrackRenderer.MSG_SET_VOLUME,
                                                                                                                                                            volume);
                                                                                                                                                }
                                                                                                                                                mVolume = volume;
                                                                                                                                            }
                                                                                                                                            

                                                                                                                                            Use an Overlay

                                                                                                                                            Use an overlay to display subtitles, messages, ads or MHEG-5 data broadcasts. By default, the overlay is disabled. You can enable it when you create the session by calling TvInputService.Session.setOverlayViewEnabled(true), as in the following example:

                                                                                                                                            @Override
                                                                                                                                            public final Session onCreateSession(String inputId) {
                                                                                                                                                BaseTvInputSessionImpl session = onCreateSessionInternal(inputId);
                                                                                                                                                session.setOverlayViewEnabled(true);
                                                                                                                                                mSessions.add(session);
                                                                                                                                                return session;
                                                                                                                                            }
                                                                                                                                            

                                                                                                                                            Use a View object for the overlay, returned from TvInputService.Session.onCreateOverlayView(), as shown here:

                                                                                                                                            @Override
                                                                                                                                            public View onCreateOverlayView() {
                                                                                                                                                LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
                                                                                                                                                View view = inflater.inflate(R.layout.overlayview, null);
                                                                                                                                                mSubtitleView = (SubtitleView) view.findViewById(R.id.subtitles);
                                                                                                                                            
                                                                                                                                                // Configure the subtitle view.
                                                                                                                                                CaptionStyleCompat captionStyle;
                                                                                                                                                float captionTextSize = getCaptionFontSize();
                                                                                                                                                captionStyle = CaptionStyleCompat.createFromCaptionStyle(
                                                                                                                                                        mCaptioningManager.getUserStyle());
                                                                                                                                                captionTextSize *= mCaptioningManager.getFontScale();
                                                                                                                                                mSubtitleView.setStyle(captionStyle);
                                                                                                                                                mSubtitleView.setTextSize(captionTextSize);
                                                                                                                                                return view;
                                                                                                                                            }
                                                                                                                                            

                                                                                                                                            The layout definition for the overlay might look something like this:

                                                                                                                                            <?xml version="1.0" encoding="utf-8"?>
                                                                                                                                            <FrameLayout
                                                                                                                                                xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                xmlns:tools="http://schemas.android.com/tools"
                                                                                                                                            
                                                                                                                                                android:layout_width="match_parent"
                                                                                                                                                android:layout_height="match_parent">
                                                                                                                                            
                                                                                                                                                <com.google.android.exoplayer.text.SubtitleView
                                                                                                                                                    android:id="@+id/subtitles"
                                                                                                                                                    android:layout_width="wrap_content"
                                                                                                                                                    android:layout_height="wrap_content"
                                                                                                                                                    android:layout_gravity="bottom|center_horizontal"
                                                                                                                                                    android:layout_marginLeft="16dp"
                                                                                                                                                    android:layout_marginRight="16dp"
                                                                                                                                                    android:layout_marginBottom="32dp"
                                                                                                                                                    android:visibility="invisible"/>
                                                                                                                                            </FrameLayout>
                                                                                                                                            

                                                                                                                                            Control Content

                                                                                                                                            When the user selects a channel, your TV input handles the onTune() callback in the TvInputService.Session object. The system TV app's parental controls determine what content displays, given the content rating. The following sections describe how to manage channel and program selection using the TvInputService.Session notify methods that communicate with the system TV app.

                                                                                                                                            Make Video Unavailable

                                                                                                                                            When the user changes the channel, you want to make sure the screen doesn't display any stray video artifacts before your TV input renders the content. When you call TvInputService.Session.onTune(), you can prevent the video from being presented by calling TvInputService.Session.notifyVideoUnavailable() and passing the VIDEO_UNAVAILABLE_REASON_TUNING constant, as shown in the following example.

                                                                                                                                            @Override
                                                                                                                                            public boolean onTune(Uri channelUri) {
                                                                                                                                                if (mSubtitleView != null) {
                                                                                                                                                    mSubtitleView.setVisibility(View.INVISIBLE);
                                                                                                                                                }
                                                                                                                                                notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
                                                                                                                                                mUnblockedRatingSet.clear();
                                                                                                                                            
                                                                                                                                                mDbHandler.removeCallbacks(mPlayCurrentProgramRunnable);
                                                                                                                                                mPlayCurrentProgramRunnable = new PlayCurrentProgramRunnable(channelUri);
                                                                                                                                                mDbHandler.post(mPlayCurrentProgramRunnable);
                                                                                                                                                return true;
                                                                                                                                            }
                                                                                                                                            

                                                                                                                                            Then, when the content is rendered to the Surface, you call TvInputService.Session.notifyVideoAvailable() to allow the video to display, like so:

                                                                                                                                            @Override
                                                                                                                                            public void onDrawnToSurface(Surface surface) {
                                                                                                                                                mFirstFrameDrawn = true;
                                                                                                                                                notifyVideoAvailable();
                                                                                                                                            }
                                                                                                                                            

                                                                                                                                            This transition lasts only for fractions of a second, but presenting a blank screen is visually better than allowing the picture to flash odd blips and jitters.

                                                                                                                                            See also, Integrate Player with Surface for more information about working with Surface to render video.

                                                                                                                                            Provide Parental Control

                                                                                                                                            To determine if a given content is blocked by parental controls and content rating, you check the TvInputManager class methods, isParentalControlsEnabled() and isRatingBlocked(android.media.tv.TvContentRating). You might also want to make sure the content's TvContentRating is included in a set of currently allowed content ratings. These considerations are shown in the following sample.

                                                                                                                                            private void checkContentBlockNeeded() {
                                                                                                                                                if (mCurrentContentRating == null || !mTvInputManager.isParentalControlsEnabled()
                                                                                                                                                        || !mTvInputManager.isRatingBlocked(mCurrentContentRating)
                                                                                                                                                        || mUnblockedRatingSet.contains(mCurrentContentRating)) {
                                                                                                                                                    // Content rating is changed so we don't need to block anymore.
                                                                                                                                                    // Unblock content here explicitly to resume playback.
                                                                                                                                                    unblockContent(null);
                                                                                                                                                    return;
                                                                                                                                                }
                                                                                                                                            
                                                                                                                                                mLastBlockedRating = mCurrentContentRating;
                                                                                                                                                if (mPlayer != null) {
                                                                                                                                                    // Children restricted content might be blocked by TV app as well,
                                                                                                                                                    // but TIF should do its best not to show any single frame of blocked content.
                                                                                                                                                    releasePlayer();
                                                                                                                                                }
                                                                                                                                            
                                                                                                                                                notifyContentBlocked(mCurrentContentRating);
                                                                                                                                            }
                                                                                                                                            

                                                                                                                                            Once you have determined if the content should or should not be blocked, notify the system TV app by calling the TvInputService.Session method notifyContentAllowed() or notifyContentBlocked() , as shown in the previous example.

                                                                                                                                            Use the TvContentRating class to generate the system-defined string for the COLUMN_CONTENT_RATING with the TvContentRating.createRating() method, as shown here:

                                                                                                                                            TvContentRating rating = TvContentRating.createRating(
                                                                                                                                                "com.android.tv",
                                                                                                                                                "US_TV",
                                                                                                                                                "US_TV_PG",
                                                                                                                                                "US_TV_D", "US_TV_L");
                                                                                                                                            

                                                                                                                                            Handle Track Selection

                                                                                                                                            The TvTrackInfo class holds information about media tracks such as the track type (video, audio, or subtitle) and so forth.

                                                                                                                                            The first time your TV input session is able to get track information, it should call TvInputService.Session.notifyTracksChanged() with a list of all tracks to update the system TV app. When there is a change in track information, call notifyTracksChanged() again to update the system.

                                                                                                                                            The system TV app provides an interface for the user to select a specific track if more than one track is available for a given track type; for example, subtitles in different languages. Your TV input responds to the onSelectTrack() call from the system TV app by calling notifyTrackSelected() , as shown in the following example. Note that when null is passed as the track ID, this deselects the track.

                                                                                                                                            @Override
                                                                                                                                            public boolean onSelectTrack(int type, String trackId) {
                                                                                                                                                if (mPlayer != null) {
                                                                                                                                                    if (type == TvTrackInfo.TYPE_SUBTITLE) {
                                                                                                                                                        if (!mCaptionEnabled && trackId != null) {
                                                                                                                                                            return false;
                                                                                                                                                        }
                                                                                                                                                        mSelectedSubtitleTrackId = trackId;
                                                                                                                                                        if (trackId == null) {
                                                                                                                                                            mSubtitleView.setVisibility(View.INVISIBLE);
                                                                                                                                                        }
                                                                                                                                                    }
                                                                                                                                                    if (mPlayer.selectTrack(type, trackId)) {
                                                                                                                                                        notifyTrackSelected(type, trackId);
                                                                                                                                                        return true;
                                                                                                                                                    }
                                                                                                                                                }
                                                                                                                                                return false;
                                                                                                                                            }
                                                                                                                                            
                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                            This class requires API level or higher

                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                            Building Apps for Auto | Android Developers Skip to content

                                                                                                                                            Most visited

                                                                                                                                            Recently visited

                                                                                                                                            navigation

                                                                                                                                              Building Apps for Auto

                                                                                                                                              Start the video course

                                                                                                                                              The Android platform enables you to extend your app to work with in-vehicle console systems running Android Auto. These systems provide a simplified interface for apps that can be used in a car, allowing users to take your app with them on the way to the grocery store or on a long road trip.

                                                                                                                                              Apps that work with Android Auto consoles run on a connected device, such as a phone or tablet. The app communicates via specific APIs with the in-dash console, which provides a user interface for the connected app that is designed for use in a car.

                                                                                                                                              For more information, follow the links below to learn how to extend your Android app to support use in vehicles.

                                                                                                                                              Get Started

                                                                                                                                              Learn the basics of extending your app for use in vehicles, with information about app configuration and user interface design considerations:

                                                                                                                                              Audio Apps

                                                                                                                                              Learn how to extend your audio app to enable content navigation and playback through a vehicle console:

                                                                                                                                              Messaging Apps

                                                                                                                                              Learn how to extend your messaging app to provide text communication services through a vehicle console:

                                                                                                                                              App Quality and Distribution

                                                                                                                                              Learn how to distribute your Auto app and how apps are evaluated for quality and driver safety:

                                                                                                                                              Video Training

                                                                                                                                              If you prefer to learn through interactive video training, check out this online course about extending your apps to work with Android Auto.

                                                                                                                                              Start the video course

                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                              This class requires API level or higher

                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                              Best Practices for Interaction and Engagement | Android Developers Skip to content

                                                                                                                                              Most visited

                                                                                                                                              Recently visited

                                                                                                                                              navigation

                                                                                                                                                Best Practices for Interaction and Engagement

                                                                                                                                                These classes teach you how to engage and retain your users by implementing the best interaction patterns for Android. For instance, to help users quickly discover content in your app, your app should match their expectations for user interaction on Android. And to keep your users coming back, you should take advantage of platform capabilities that reveal and open your content without requiring users to go through the app launcher.

                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                This class requires API level or higher

                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                Designing Effective Navigation | Android Developers Skip to content

                                                                                                                                                Most visited

                                                                                                                                                Recently visited

                                                                                                                                                navigation

                                                                                                                                                  Designing Effective Navigation

                                                                                                                                                  Dependencies and prerequisites

                                                                                                                                                  This class is not specific to any particular version of the Android platform. It is also primarily design-focused and does not require knowledge of the Android SDK. That said, you should have experience using an Android device for a better understanding of the context in which Android applications run.

                                                                                                                                                  You should also have basic familiarity with the Action Bar (pattern docs at Android Design), used across most applications in devices running Android 3.0 and later.

                                                                                                                                                  One of the very first steps to designing and developing an Android application is to determine what users are able to see and do with the app. Once you know what kinds of data users are interacting with in the app, the next step is to design the interactions that allow users to navigate across, into, and back out from the different pieces of content within the app.

                                                                                                                                                  This class shows you how to plan out the high-level screen hierarchy for your application and then choose appropriate forms of navigation to allow users to effectively and intuitively traverse your content. Each lesson covers various stages in the interaction design process for navigation in Android applications, in roughly chronological order. After going through the lessons in this class, you should be able to apply the methodology and navigation paradigms outlined here to your own applications, providing a coherent navigation experience for your users.

                                                                                                                                                  Lessons

                                                                                                                                                  Planning Screens and Their Relationships
                                                                                                                                                  Learn how to choose which screens your application should contain. Also learn how to choose which screens should be directly reachable from others. This lesson introduces a hypothetical news application to serve as an example for later lessons.
                                                                                                                                                  Planning for Multiple Touchscreen Sizes
                                                                                                                                                  Learn how to group related screens together on larger-screen devices to optimize use of available screen space.
                                                                                                                                                  Providing Descendant and Lateral Navigation
                                                                                                                                                  Learn about techniques for allowing users to navigate deep into, as well as across, your content hierarchy. Also learn about pros and cons of, and best practices for, specific navigational UI elements for various situations.
                                                                                                                                                  Providing Ancestral and Temporal Navigation
                                                                                                                                                  Learn how to allow users to navigate upwards in the content hierarchy. Also learn about best practices for the Back button and temporal navigation, or navigation to previous screens that may not be hierarchically related.
                                                                                                                                                  Putting it All Together: Wireframing the Example App
                                                                                                                                                  Learn how to create screen wireframes (low-fidelity graphic mockups) representing the screens in a news application based on the desired information model. These wireframes utilize navigational elements discussed in previous lessons to demonstrate intuitive and efficient navigation.
                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                  This class requires API level or higher

                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                  Implementing Effective Navigation | Android Developers Skip to content

                                                                                                                                                  Most visited

                                                                                                                                                  Recently visited

                                                                                                                                                  navigation

                                                                                                                                                    Implementing Effective Navigation

                                                                                                                                                    Dependencies and prerequisites

                                                                                                                                                    You should also read

                                                                                                                                                    Try it out

                                                                                                                                                    Download the sample app

                                                                                                                                                    EffectiveNavigation.zip

                                                                                                                                                    This class demonstrates how to implement the key navigation design patterns detailed in the Designing Effective Navigation class.

                                                                                                                                                    After reading the lessons in this class, you should have a strong understanding of how to implement navigation patterns with tabs, swipe views, and a navigation drawer. You should also understand how to provide proper Up and Back navigation.

                                                                                                                                                    Note: Several elements of this class require the Support Library APIs. If you have not used the Support Library before, follow the instructions in the Support Library Setup document.

                                                                                                                                                    Lessons

                                                                                                                                                    Creating Swipe Views with Tabs
                                                                                                                                                    Learn how to implement tabs in the action bar and provide horizontal paging (swipe views) to navigate between tabs.
                                                                                                                                                    Creating a Navigation Drawer
                                                                                                                                                    Learn how to build an interface with a hidden navigation drawer on the side of the screen that opens with a swipe or by pressing the action bar's app icon.
                                                                                                                                                    Providing Up Navigation
                                                                                                                                                    Learn how to implement Up navigation using the action bar's app icon.
                                                                                                                                                    Providing Proper Back Navigation
                                                                                                                                                    Learn how to correctly handle the Back button in special cases, including how to insert activities into the back stack when deep-linking the user from notifications or app widgets.
                                                                                                                                                    Implementing Descendant Navigation
                                                                                                                                                    Learn the finer points of navigating down into your application's information hierarchy.
                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                    This class requires API level or higher

                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                    Notifying the User | Android Developers Skip to content

                                                                                                                                                    Most visited

                                                                                                                                                    Recently visited

                                                                                                                                                    navigation

                                                                                                                                                      Notifying the User

                                                                                                                                                      Dependencies and prerequisites

                                                                                                                                                      • Android 1.6 (API Level 4) or higher

                                                                                                                                                      You should also read

                                                                                                                                                      Try it out

                                                                                                                                                      Download the sample

                                                                                                                                                      NotifyUser.zip

                                                                                                                                                      A notification is a user interface element that you display outside your app's normal UI to indicate that an event has occurred. Users can choose to view the notification while using other apps and respond to it when it's convenient for them.

                                                                                                                                                      The Notifications design guide shows you how to design effective notifications and when to use them. This class shows you how to implement the most common notification designs.

                                                                                                                                                      Lessons

                                                                                                                                                      Building a Notification
                                                                                                                                                      Learn how to create a notification Builder, set the required features, and issue the notification.
                                                                                                                                                      Preserving Navigation when Starting an Activity
                                                                                                                                                      Learn how to enforce the proper navigation for an Activity started from a notification.
                                                                                                                                                      Updating Notifications
                                                                                                                                                      Learn how to update and remove notifications.
                                                                                                                                                      Using Big View Styles
                                                                                                                                                      Learn how to create a big view within an expanded notification, while still maintaining backward compatibility.
                                                                                                                                                      Displaying Progress in a Notification
                                                                                                                                                      Learn how to display the progress of an operation in a notification, both for operations where you can estimate how much has been completed (determinate progress) and operations where you don't know how much has been completed (indefinite progress).
                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                      This class requires API level or higher

                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                      Supporting Swipe-to-Refresh | Android Developers Skip to content

                                                                                                                                                      Most visited

                                                                                                                                                      Recently visited

                                                                                                                                                      navigation

                                                                                                                                                        Supporting Swipe-to-Refresh

                                                                                                                                                        Dependencies and prerequisites

                                                                                                                                                        • Android 1.6 (API level 4) or later
                                                                                                                                                        • Latest version of the Android v4 Support Library

                                                                                                                                                        Sample Apps

                                                                                                                                                        Even if your app automatically updates its content on a regular basis, you can allow users to request manual updates as well. For example, a weather forecasting app can allow users get the latest forecasts on demand. To provide a standard user experience for requesting updates, the Android platform includes the swipe-to-refresh design pattern, which allows users to trigger an update with a vertical swipe.

                                                                                                                                                        Note: This class requires the latest version of the Android v4 Support Library APIs. If you have not used the Support Library before, follow the instructions in the Support Library Setup document.

                                                                                                                                                        Lessons

                                                                                                                                                        Adding Swipe-to-Refresh To Your App
                                                                                                                                                        To replay the movie, click on the device screen
                                                                                                                                                        Learn how to provide swipe-to-refresh support in a ListView or GridView, and how to provide an accessible refresh option using the action bar.
                                                                                                                                                        Responding to a Refresh Request
                                                                                                                                                        Learn how to respond to the swipe-to-refresh gesture, and how to perform the same update from an action bar action.
                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                        This class requires API level or higher

                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                        Adding Swipe-to-Refresh To Your App | Android Developers Skip to content

                                                                                                                                                        Most visited

                                                                                                                                                        Recently visited

                                                                                                                                                        navigation

                                                                                                                                                          Adding Swipe-to-Refresh To Your App

                                                                                                                                                          The swipe-to-refresh user interface pattern is implemented entirely within the SwipeRefreshLayout widget, which detects the vertical swipe, displays a distinctive progress bar, and triggers callback methods in your app. You enable this behavior by adding the widget to your layout file as the parent of a ListView or GridView, and implementing the refresh behavior that gets invoked when the user swipes.

                                                                                                                                                          This lesson shows you how to add the widget to an existing layout. It also shows you how to add a refresh action to the action bar overflow area, so that users who may be unable to use the swipe gesture can trigger a manual update with an external device.

                                                                                                                                                          Add the SwipeRefreshLayout Widget

                                                                                                                                                          To add the swipe to refresh widget to an existing app, add SwipeRefreshLayout as the parent of a single ListView or GridView. Remember that SwipeRefreshLayout only supports a single ListView or GridView child.

                                                                                                                                                          The following example demonstrates how to add the SwipeRefreshLayout widget to an existing layout file containing a ListView:

                                                                                                                                                          <android.support.v4.widget.SwipeRefreshLayout
                                                                                                                                                              xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                              android:id="@+id/swiperefresh"
                                                                                                                                                              android:layout_width="match_parent"
                                                                                                                                                              android:layout_height="match_parent">
                                                                                                                                                          
                                                                                                                                                              <ListView
                                                                                                                                                                  android:id="@android:id/list"
                                                                                                                                                                  android:layout_width="match_parent"
                                                                                                                                                                  android:layout_height="match_parent" />
                                                                                                                                                          
                                                                                                                                                          </android.support.v4.widget.SwipeRefreshLayout>

                                                                                                                                                          You can also use the SwipeRefreshLayout widget with a ListFragment. If the layout contains a ListView with the ID "@android:id/list", the swipe-to-refresh functionality is automatically supported. However, explicitly declaring the ListView in this way supersedes the default ListFragment view structure. If you want to use the default view structure, you will have to override parts of the SwipeRefreshLayout and ListFragment behavior. For an example of how to do this, see the SwipeRefreshListFragment sample app.

                                                                                                                                                          Add a Refresh Action to the Action Bar

                                                                                                                                                          You should add a refresh action to your app's action bar to ensure that users who may not be able to perform a swipe gesture can still trigger a manual update. For example, users with accessibility issues can trigger action bar actions using external devices, such as keyboards and D-pads.

                                                                                                                                                          You should add the refresh action as a menu item, rather than as a button, by setting the attribute android:showAsAction=never. If you display the action as a button, users may assume that the refresh button action is different from the swipe-to-refresh action. By making the refresh action less conspicuous in the action bar, you can encourage users to perform manual updates with the swipe gesture while still maintaining the accessible option in a place where D-pad users would look for it.

                                                                                                                                                          The following code demonstrates how to add the swipe-to-refresh action to the overflow area:

                                                                                                                                                          <menu xmlns:android="http://schemas.android.com/apk/res/android" >
                                                                                                                                                              <item
                                                                                                                                                                  android:id="@+id/menu_refresh"
                                                                                                                                                                  android:showAsAction="never"
                                                                                                                                                                  android:title="@string/menu_refresh"/>
                                                                                                                                                          </menu>
                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                          This class requires API level or higher

                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                          Responding to a Refresh Request | Android Developers Skip to content

                                                                                                                                                          Most visited

                                                                                                                                                          Recently visited

                                                                                                                                                          navigation

                                                                                                                                                            Responding to a Refresh Request

                                                                                                                                                            This lesson shows you how to update your app when the user requests a manual refresh, whether the user triggers the refresh with a swipe gesture or by using the action bar refresh action.

                                                                                                                                                            Respond to the Refresh Gesture

                                                                                                                                                            When the user makes a swipe gesture, the system displays the progress indicator and calls your app's callback method. Your callback method is responsible for actually updating the app's data.

                                                                                                                                                            To respond to the refresh gesture in your app, implement the SwipeRefreshLayout.OnRefreshListener interface and its onRefresh() method. The onRefresh() method is invoked when the user performs a swipe gesture.

                                                                                                                                                            You should put the code for the actual update operation in a separate method, and call that update method from your onRefresh() implementation. That way, you can use the same update method to perform the update when the user triggers a refresh from the action bar.

                                                                                                                                                            Your update method calls setRefreshing(false) when it has finished updating the data. Calling this method instructs the SwipeRefreshLayout to remove the progress indicator and update the view contents.

                                                                                                                                                            For example, the following code implements onRefresh() and invokes the method myUpdateOperation() to update the data displayed by the ListView:

                                                                                                                                                            /*
                                                                                                                                                             * Sets up a SwipeRefreshLayout.OnRefreshListener that is invoked when the user
                                                                                                                                                             * performs a swipe-to-refresh gesture.
                                                                                                                                                             */
                                                                                                                                                            mySwipeRefreshLayout.setOnRefreshListener(
                                                                                                                                                                new SwipeRefreshLayout.OnRefreshListener() {
                                                                                                                                                                    @Override
                                                                                                                                                                    public void onRefresh() {
                                                                                                                                                                        Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout");
                                                                                                                                                            
                                                                                                                                                                        // This method performs the actual data-refresh operation.
                                                                                                                                                                        // The method calls setRefreshing(false) when it's finished.
                                                                                                                                                                        myUpdateOperation();
                                                                                                                                                                    }
                                                                                                                                                                }
                                                                                                                                                            );

                                                                                                                                                            Respond to the Refresh Action

                                                                                                                                                            If the user requests a refresh by using the action bar, the system calls the onOptionsItemSelected() method. Your app should respond to this call by displaying the progress indicator and refreshing the app's data.

                                                                                                                                                            To respond to the refresh action, override onOptionsItemSelected(). In your override method, trigger the SwipeRefreshLayout progress indicator by calling setRefreshing() with the value true, then perform the update operation. Once again, you should be doing the actual update in a separate method, so the same method can be called whether the user triggers the update with a swipe or by using the action bar. When the update has finished, call setRefreshing(false) to remove the refresh progress indicator.

                                                                                                                                                            The following code shows how to respond to the request action:

                                                                                                                                                            /*
                                                                                                                                                             * Listen for option item selections so that we receive a notification
                                                                                                                                                             * when the user requests a refresh by selecting the refresh action bar item.
                                                                                                                                                             */
                                                                                                                                                            @Override
                                                                                                                                                            public boolean onOptionsItemSelected(MenuItem item) {
                                                                                                                                                                switch (item.getItemId()) {
                                                                                                                                                            
                                                                                                                                                                    // Check if user triggered a refresh:
                                                                                                                                                                    case R.id.menu_refresh:
                                                                                                                                                                        Log.i(LOG_TAG, "Refresh menu item selected");
                                                                                                                                                            
                                                                                                                                                                        // Signal SwipeRefreshLayout to start the progress indicator
                                                                                                                                                                        mySwipeRefreshLayout.setRefreshing(true);
                                                                                                                                                            
                                                                                                                                                                        // Start the refresh background task.
                                                                                                                                                                        // This method calls setRefreshing(false) when it's finished.
                                                                                                                                                                        myUpdateOperation();
                                                                                                                                                            
                                                                                                                                                                        return true;
                                                                                                                                                                }
                                                                                                                                                            
                                                                                                                                                                // User didn't trigger a refresh, let the superclass handle this action
                                                                                                                                                                return super.onOptionsItemSelected(item);
                                                                                                                                                            
                                                                                                                                                            }

                                                                                                                                                            Note: When the user triggers a refresh with a swipe action as described in Respond to the Refresh Gesture, you do not need to call setRefreshing(). The SwipeRefreshLayout widget takes care of displaying the progress indicator and removing it when the update has finished. However, if the update is triggered by any means other than a swipe gesture, you need to explicitly turn the progress indicator on with setRefreshing(). The method which actually refreshes the data calls setRefreshing(false) to signal that the update is finished.

                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                            This class requires API level or higher

                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                            Adding Search Functionality | Android Developers Skip to content

                                                                                                                                                            Most visited

                                                                                                                                                            Recently visited

                                                                                                                                                            navigation

                                                                                                                                                              Adding Search Functionality

                                                                                                                                                              Dependencies and prerequisites

                                                                                                                                                              • Android 3.0 or later (with some support for Android 2.1)
                                                                                                                                                              • Experience building an Android User Interface

                                                                                                                                                              You should also read

                                                                                                                                                              Android's built-in search features offer apps an easy way to provide a consistent search experience for all users. There are two ways to implement search in your app depending on the version of Android that is running on the device. This class covers how to add search with SearchView, which was introduced in Android 3.0, while maintaining backward compatibility with older versions of Android by using the default search dialog provided by the system.

                                                                                                                                                              Lessons

                                                                                                                                                              Setting Up the Search Interface
                                                                                                                                                              Learn how to add a search interface to your app and how to configure an activity to handle search queries.
                                                                                                                                                              Storing and Searching for Data
                                                                                                                                                              Learn a simple way to store and search for data in a SQLite virtual database table.
                                                                                                                                                              Remaining Backward Compatible
                                                                                                                                                              Learn how to keep search features backward compatible with older devices.
                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                              This class requires API level or higher

                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                              Setting Up the Search Interface | Android Developers Skip to content

                                                                                                                                                              Most visited

                                                                                                                                                              Recently visited

                                                                                                                                                              navigation

                                                                                                                                                                Setting Up the Search Interface

                                                                                                                                                                Beginning in Android 3.0, using the SearchView widget as an item in the app bar is the preferred way to provide search in your app. Like with all items in the app bar, you can define the SearchView to show at all times, only when there is room, or as a collapsible action, which displays the SearchView as an icon initially, then takes up the entire app bar as a search field when the user clicks the icon.

                                                                                                                                                                Note: Later in this class, you will learn how to make your app compatible down to Android 2.1 (API level 7) for devices that do not support SearchView.

                                                                                                                                                                Add the Search View to the App Bar

                                                                                                                                                                To add a SearchView widget to the app bar, create a file named res/menu/options_menu.xml in your project and add the following code to the file. This code defines how to create the search item, such as the icon to use and the title of the item. The collapseActionView attribute allows your SearchView to expand to take up the whole app bar and collapse back down into a normal app bar item when not in use. Because of the limited app bar space on handset devices, using the collapsibleActionView attribute is recommended to provide a better user experience.

                                                                                                                                                                <?xml version="1.0" encoding="utf-8"?>
                                                                                                                                                                <menu xmlns:android="http://schemas.android.com/apk/res/android">
                                                                                                                                                                    <item android:id="@+id/search"
                                                                                                                                                                          android:title="@string/search_title"
                                                                                                                                                                          android:icon="@drawable/ic_search"
                                                                                                                                                                          android:showAsAction="collapseActionView|ifRoom"
                                                                                                                                                                          android:actionViewClass="android.widget.SearchView" />
                                                                                                                                                                </menu>
                                                                                                                                                                

                                                                                                                                                                Note: If you already have an existing XML file for your menu items, you can add the <item> element to that file instead.

                                                                                                                                                                To display the SearchView in the app bar, inflate the XML menu resource (res/menu/options_menu.xml) in the onCreateOptionsMenu() method of your activity:

                                                                                                                                                                @Override
                                                                                                                                                                public boolean onCreateOptionsMenu(Menu menu) {
                                                                                                                                                                    MenuInflater inflater = getMenuInflater();
                                                                                                                                                                    inflater.inflate(R.menu.options_menu, menu);
                                                                                                                                                                
                                                                                                                                                                    return true;
                                                                                                                                                                }
                                                                                                                                                                

                                                                                                                                                                If you run your app now, the SearchView appears in your app's app bar, but it isn't functional. You now need to define how the SearchView behaves.

                                                                                                                                                                Create a Searchable Configuration

                                                                                                                                                                A searchable configuration defines how the SearchView behaves and is defined in a res/xml/searchable.xml file. At a minimum, a searchable configuration must contain an android:label attribute that has the same value as the android:label attribute of the <application> or <activity> element in your Android manifest. However, we also recommend adding an android:hint attribute to give the user an idea of what to enter into the search box:

                                                                                                                                                                <?xml version="1.0" encoding="utf-8"?>
                                                                                                                                                                
                                                                                                                                                                <searchable xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                        android:label="@string/app_name"
                                                                                                                                                                        android:hint="@string/search_hint" />
                                                                                                                                                                

                                                                                                                                                                In your application's manifest file, declare a <meta-data> element that points to the res/xml/searchable.xml file, so that your application knows where to find it. Declare the element in an <activity> that you want to display the SearchView in:

                                                                                                                                                                <activity ... >
                                                                                                                                                                    ...
                                                                                                                                                                    <meta-data android:name="android.app.searchable"
                                                                                                                                                                            android:resource="@xml/searchable" />
                                                                                                                                                                
                                                                                                                                                                </activity>
                                                                                                                                                                

                                                                                                                                                                In the onCreateOptionsMenu() method that you created before, associate the searchable configuration with the SearchView by calling setSearchableInfo(SearchableInfo):

                                                                                                                                                                @Override
                                                                                                                                                                public boolean onCreateOptionsMenu(Menu menu) {
                                                                                                                                                                    MenuInflater inflater = getMenuInflater();
                                                                                                                                                                    inflater.inflate(R.menu.options_menu, menu);
                                                                                                                                                                
                                                                                                                                                                    // Associate searchable configuration with the SearchView
                                                                                                                                                                    SearchManager searchManager =
                                                                                                                                                                           (SearchManager) getSystemService(Context.SEARCH_SERVICE);
                                                                                                                                                                    SearchView searchView =
                                                                                                                                                                            (SearchView) menu.findItem(R.id.search).getActionView();
                                                                                                                                                                    searchView.setSearchableInfo(
                                                                                                                                                                            searchManager.getSearchableInfo(getComponentName()));
                                                                                                                                                                
                                                                                                                                                                    return true;
                                                                                                                                                                }
                                                                                                                                                                

                                                                                                                                                                The call to getSearchableInfo() obtains a SearchableInfo object that is created from the searchable configuration XML file. When the searchable configuration is correctly associated with your SearchView, the SearchView starts an activity with the ACTION_SEARCH intent when a user submits a query. You now need an activity that can filter for this intent and handle the search query.

                                                                                                                                                                Create a Searchable Activity

                                                                                                                                                                A SearchView tries to start an activity with the ACTION_SEARCH when a user submits a search query. A searchable activity filters for the ACTION_SEARCH intent and searches for the query in some sort of data set. To create a searchable activity, declare an activity of your choice to filter for the ACTION_SEARCH intent:

                                                                                                                                                                <activity android:name=".SearchResultsActivity" ... >
                                                                                                                                                                    ...
                                                                                                                                                                    <intent-filter>
                                                                                                                                                                        <action android:name="android.intent.action.SEARCH" />
                                                                                                                                                                    </intent-filter>
                                                                                                                                                                    ...
                                                                                                                                                                </activity>
                                                                                                                                                                

                                                                                                                                                                In your searchable activity, handle the ACTION_SEARCH intent by checking for it in your onCreate() method.

                                                                                                                                                                Note: If your searchable activity launches in single top mode (android:launchMode="singleTop"), also handle the ACTION_SEARCH intent in the onNewIntent() method. In single top mode, only one instance of your activity is created and subsequent calls to start your activity do not create a new activity on the stack. This launch mode is useful so users can perform searches from the same activity without creating a new activity instance every time.

                                                                                                                                                                public class SearchResultsActivity extends Activity {
                                                                                                                                                                
                                                                                                                                                                    @Override
                                                                                                                                                                    public void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                        ...
                                                                                                                                                                        handleIntent(getIntent());
                                                                                                                                                                    }
                                                                                                                                                                
                                                                                                                                                                    @Override
                                                                                                                                                                    protected void onNewIntent(Intent intent) {
                                                                                                                                                                        ...
                                                                                                                                                                        handleIntent(intent);
                                                                                                                                                                    }
                                                                                                                                                                
                                                                                                                                                                    private void handleIntent(Intent intent) {
                                                                                                                                                                
                                                                                                                                                                        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
                                                                                                                                                                            String query = intent.getStringExtra(SearchManager.QUERY);
                                                                                                                                                                            //use the query to search your data somehow
                                                                                                                                                                        }
                                                                                                                                                                    }
                                                                                                                                                                    ...
                                                                                                                                                                }
                                                                                                                                                                

                                                                                                                                                                If you run your app now, the SearchView can accept the user's query and start your searchable activity with the ACTION_SEARCH intent. It is now up to you to figure out how to store and search your data given a query.

                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                Storing and Searching for Data | Android Developers Skip to content

                                                                                                                                                                Most visited

                                                                                                                                                                Recently visited

                                                                                                                                                                navigation

                                                                                                                                                                  Storing and Searching for Data

                                                                                                                                                                  There are many ways to store your data, such as in an online database, in a local SQLite database, or even in a text file. It is up to you to decide what is the best solution for your application. This lesson shows you how to create a SQLite virtual table that can provide robust full-text searching. The table is populated with data from a text file that contains a word and definition pair on each line in the file.

                                                                                                                                                                  Create the Virtual Table

                                                                                                                                                                  A virtual table behaves similarly to a SQLite table, but reads and writes to an object in memory via callbacks, instead of to a database file. To create a virtual table, create a class for the table:

                                                                                                                                                                  public class DatabaseTable {
                                                                                                                                                                      private final DatabaseOpenHelper mDatabaseOpenHelper;
                                                                                                                                                                  
                                                                                                                                                                      public DatabaseTable(Context context) {
                                                                                                                                                                          mDatabaseOpenHelper = new DatabaseOpenHelper(context);
                                                                                                                                                                      }
                                                                                                                                                                  }
                                                                                                                                                                  

                                                                                                                                                                  Create an inner class in DatabaseTable that extends SQLiteOpenHelper. The SQLiteOpenHelper class defines abstract methods that you must override so that your database table can be created and upgraded when necessary. For example, here is some code that declares a database table that will contain words for a dictionary app:

                                                                                                                                                                  public class DatabaseTable {
                                                                                                                                                                  
                                                                                                                                                                      private static final String TAG = "DictionaryDatabase";
                                                                                                                                                                  
                                                                                                                                                                      //The columns we'll include in the dictionary table
                                                                                                                                                                      public static final String COL_WORD = "WORD";
                                                                                                                                                                      public static final String COL_DEFINITION = "DEFINITION";
                                                                                                                                                                  
                                                                                                                                                                      private static final String DATABASE_NAME = "DICTIONARY";
                                                                                                                                                                      private static final String FTS_VIRTUAL_TABLE = "FTS";
                                                                                                                                                                      private static final int DATABASE_VERSION = 1;
                                                                                                                                                                  
                                                                                                                                                                      private final DatabaseOpenHelper mDatabaseOpenHelper;
                                                                                                                                                                  
                                                                                                                                                                      public DatabaseTable(Context context) {
                                                                                                                                                                          mDatabaseOpenHelper = new DatabaseOpenHelper(context);
                                                                                                                                                                      }
                                                                                                                                                                  
                                                                                                                                                                      private static class DatabaseOpenHelper extends SQLiteOpenHelper {
                                                                                                                                                                  
                                                                                                                                                                          private final Context mHelperContext;
                                                                                                                                                                          private SQLiteDatabase mDatabase;
                                                                                                                                                                  
                                                                                                                                                                          private static final String FTS_TABLE_CREATE =
                                                                                                                                                                                      "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
                                                                                                                                                                                      " USING fts3 (" +
                                                                                                                                                                                      COL_WORD + ", " +
                                                                                                                                                                                      COL_DEFINITION + ")";
                                                                                                                                                                  
                                                                                                                                                                          DatabaseOpenHelper(Context context) {
                                                                                                                                                                              super(context, DATABASE_NAME, null, DATABASE_VERSION);
                                                                                                                                                                              mHelperContext = context;
                                                                                                                                                                          }
                                                                                                                                                                  
                                                                                                                                                                          @Override
                                                                                                                                                                          public void onCreate(SQLiteDatabase db) {
                                                                                                                                                                              mDatabase = db;
                                                                                                                                                                              mDatabase.execSQL(FTS_TABLE_CREATE);
                                                                                                                                                                          }
                                                                                                                                                                  
                                                                                                                                                                          @Override
                                                                                                                                                                          public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                                                                                                                                                                              Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                                                                                                                                                                                      + newVersion + ", which will destroy all old data");
                                                                                                                                                                              db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
                                                                                                                                                                              onCreate(db);
                                                                                                                                                                          }
                                                                                                                                                                      }
                                                                                                                                                                  }
                                                                                                                                                                  

                                                                                                                                                                  Populate the Virtual Table

                                                                                                                                                                  The table now needs data to store. The following code shows you how to read a text file (located in res/raw/definitions.txt) that contains words and their definitions, how to parse that file, and how to insert each line of that file as a row in the virtual table. This is all done in another thread to prevent the UI from locking. Add the following code to your DatabaseOpenHelper inner class.

                                                                                                                                                                  Tip: You also might want to set up a callback to notify your UI activity of this thread's completion.

                                                                                                                                                                  private void loadDictionary() {
                                                                                                                                                                          new Thread(new Runnable() {
                                                                                                                                                                              public void run() {
                                                                                                                                                                                  try {
                                                                                                                                                                                      loadWords();
                                                                                                                                                                                  } catch (IOException e) {
                                                                                                                                                                                      throw new RuntimeException(e);
                                                                                                                                                                                  }
                                                                                                                                                                              }
                                                                                                                                                                          }).start();
                                                                                                                                                                      }
                                                                                                                                                                  
                                                                                                                                                                  private void loadWords() throws IOException {
                                                                                                                                                                      final Resources resources = mHelperContext.getResources();
                                                                                                                                                                      InputStream inputStream = resources.openRawResource(R.raw.definitions);
                                                                                                                                                                      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                                                                                                                                                                  
                                                                                                                                                                      try {
                                                                                                                                                                          String line;
                                                                                                                                                                          while ((line = reader.readLine()) != null) {
                                                                                                                                                                              String[] strings = TextUtils.split(line, "-");
                                                                                                                                                                              if (strings.length < 2) continue;
                                                                                                                                                                              long id = addWord(strings[0].trim(), strings[1].trim());
                                                                                                                                                                              if (id < 0) {
                                                                                                                                                                                  Log.e(TAG, "unable to add word: " + strings[0].trim());
                                                                                                                                                                              }
                                                                                                                                                                          }
                                                                                                                                                                      } finally {
                                                                                                                                                                          reader.close();
                                                                                                                                                                      }
                                                                                                                                                                  }
                                                                                                                                                                  
                                                                                                                                                                  public long addWord(String word, String definition) {
                                                                                                                                                                      ContentValues initialValues = new ContentValues();
                                                                                                                                                                      initialValues.put(COL_WORD, word);
                                                                                                                                                                      initialValues.put(COL_DEFINITION, definition);
                                                                                                                                                                  
                                                                                                                                                                      return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
                                                                                                                                                                  }
                                                                                                                                                                  

                                                                                                                                                                  Call the loadDictionary() method wherever appropriate to populate the table. A good place would be in the onCreate() method of the DatabaseOpenHelper class, right after you create the table:

                                                                                                                                                                  @Override
                                                                                                                                                                  public void onCreate(SQLiteDatabase db) {
                                                                                                                                                                      mDatabase = db;
                                                                                                                                                                      mDatabase.execSQL(FTS_TABLE_CREATE);
                                                                                                                                                                      loadDictionary();
                                                                                                                                                                  }
                                                                                                                                                                  

                                                                                                                                                                  When you have the virtual table created and populated, use the query supplied by your SearchView to search the data. Add the following methods to the DatabaseTable class to build a SQL statement that searches for the query:

                                                                                                                                                                  public Cursor getWordMatches(String query, String[] columns) {
                                                                                                                                                                      String selection = COL_WORD + " MATCH ?";
                                                                                                                                                                      String[] selectionArgs = new String[] {query+"*"};
                                                                                                                                                                  
                                                                                                                                                                      return query(selection, selectionArgs, columns);
                                                                                                                                                                  }
                                                                                                                                                                  
                                                                                                                                                                  private Cursor query(String selection, String[] selectionArgs, String[] columns) {
                                                                                                                                                                      SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
                                                                                                                                                                      builder.setTables(FTS_VIRTUAL_TABLE);
                                                                                                                                                                  
                                                                                                                                                                      Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(),
                                                                                                                                                                              columns, selection, selectionArgs, null, null, null);
                                                                                                                                                                  
                                                                                                                                                                      if (cursor == null) {
                                                                                                                                                                          return null;
                                                                                                                                                                      } else if (!cursor.moveToFirst()) {
                                                                                                                                                                          cursor.close();
                                                                                                                                                                          return null;
                                                                                                                                                                      }
                                                                                                                                                                      return cursor;
                                                                                                                                                                  }
                                                                                                                                                                  

                                                                                                                                                                  Search for a query by calling getWordMatches(). Any matching results are returned in a Cursor that you can iterate through or use to build a ListView. This example calls getWordMatches() in the handleIntent() method of the searchable activity. Remember that the searchable activity receives the query inside of the ACTION_SEARCH intent as an extra, because of the intent filter that you previously created:

                                                                                                                                                                  DatabaseTable db = new DatabaseTable(this);
                                                                                                                                                                  
                                                                                                                                                                  ...
                                                                                                                                                                  
                                                                                                                                                                  private void handleIntent(Intent intent) {
                                                                                                                                                                  
                                                                                                                                                                      if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
                                                                                                                                                                          String query = intent.getStringExtra(SearchManager.QUERY);
                                                                                                                                                                          Cursor c = db.getWordMatches(query, null);
                                                                                                                                                                          //process Cursor and display results
                                                                                                                                                                      }
                                                                                                                                                                  }
                                                                                                                                                                  
                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                  Remaining Backward Compatible | Android Developers Skip to content

                                                                                                                                                                  Most visited

                                                                                                                                                                  Recently visited

                                                                                                                                                                  navigation

                                                                                                                                                                    Remaining Backward Compatible

                                                                                                                                                                    The SearchView and action bar are only available on Android 3.0 and later. To support older platforms, you can fall back to the search dialog. The search dialog is a system provided UI that overlays on top of your application when invoked.

                                                                                                                                                                    Set Minimum and Target API levels

                                                                                                                                                                    To setup the search dialog, first declare in your manifest that you want to support older devices, but want to target Android 3.0 or later versions. When you do this, your application automatically uses the action bar on Android 3.0 or later and uses the traditional menu system on older devices:

                                                                                                                                                                    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15" />
                                                                                                                                                                    
                                                                                                                                                                    <application>
                                                                                                                                                                    ...
                                                                                                                                                                    

                                                                                                                                                                    Provide the Search Dialog for Older Devices

                                                                                                                                                                    To invoke the search dialog on older devices, call onSearchRequested() whenever a user selects the search menu item from the options menu. Because Android 3.0 and higher devices show the SearchView in the action bar (as demonstrated in the first lesson), only versions older than 3.0 call onOptionsItemSelected() when the user selects the search menu item.

                                                                                                                                                                    @Override
                                                                                                                                                                    public boolean onOptionsItemSelected(MenuItem item) {
                                                                                                                                                                        switch (item.getItemId()) {
                                                                                                                                                                            case R.id.search:
                                                                                                                                                                                onSearchRequested();
                                                                                                                                                                                return true;
                                                                                                                                                                            default:
                                                                                                                                                                                return false;
                                                                                                                                                                        }
                                                                                                                                                                    }
                                                                                                                                                                    

                                                                                                                                                                    Check the Android Build Version at Runtime

                                                                                                                                                                    At runtime, check the device version to make sure an unsupported use of SearchView does not occur on older devices. In our example code, this happens in the onCreateOptionsMenu() method:

                                                                                                                                                                    @Override
                                                                                                                                                                    public boolean onCreateOptionsMenu(Menu menu) {
                                                                                                                                                                    
                                                                                                                                                                        MenuInflater inflater = getMenuInflater();
                                                                                                                                                                        inflater.inflate(R.menu.options_menu, menu);
                                                                                                                                                                    
                                                                                                                                                                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                                                                                                                                                                            SearchManager searchManager =
                                                                                                                                                                                    (SearchManager) getSystemService(Context.SEARCH_SERVICE);
                                                                                                                                                                            SearchView searchView =
                                                                                                                                                                                    (SearchView) menu.findItem(R.id.search).getActionView();
                                                                                                                                                                            searchView.setSearchableInfo(
                                                                                                                                                                                    searchManager.getSearchableInfo(getComponentName()));
                                                                                                                                                                            searchView.setIconifiedByDefault(false);
                                                                                                                                                                        }
                                                                                                                                                                        return true;
                                                                                                                                                                    }
                                                                                                                                                                    
                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                    Making Your App Content Searchable by Google | Android Developers Skip to content

                                                                                                                                                                    Most visited

                                                                                                                                                                    Recently visited

                                                                                                                                                                    navigation

                                                                                                                                                                      Making Your App Content Searchable by Google

                                                                                                                                                                      Video

                                                                                                                                                                      DevBytes: App Indexing

                                                                                                                                                                      As mobile apps become more pervasive, users are looking for relevant information not only from web sites but also from apps they have installed. You can enable Google to crawl through your app content and present your Android app as a destination to users through Google Search results, when that content corresponds to a web page that you own.

                                                                                                                                                                      You can make it possible for Google Search to open specific content in your app by providing intent filters for your activities. Google Search app indexing complements this capability by presenting links to relevant app content alongside links to your web pages in users' search results. Users on mobile devices can then click on a link to open your app from their search results, allowing them to directly view your app's content instead of a web page.

                                                                                                                                                                      To enable Google Search app indexing, you need to provide Google with information about the relationship between your app and web site. This process involves the following steps:

                                                                                                                                                                      1. Enable deep linking to specific content in your app by adding intent filters in your app manifest.
                                                                                                                                                                      2. Annotate these links in the associated web pages on your web site or in a Sitemap file.
                                                                                                                                                                      3. Opt in to allow Googlebot to crawl through your APK in the Google Play store to index your app content. You are automatically opted-in when you join as a participant in the early adopter program.

                                                                                                                                                                      This class shows how to enable deep linking and indexing of your application content so that users can open this content directly from mobile search results.

                                                                                                                                                                      Lessons

                                                                                                                                                                      Enabling Deep Links for App Content
                                                                                                                                                                      Shows how to add intent filters to enable deep linking to app content.
                                                                                                                                                                      Specifying App Content for Indexing
                                                                                                                                                                      Shows how to annotate web site metadata to allow Google's algorithms to index app content.
                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                      Enabling Deep Links for App Content | Android Developers Skip to content

                                                                                                                                                                      Most visited

                                                                                                                                                                      Recently visited

                                                                                                                                                                      navigation

                                                                                                                                                                        Enabling Deep Links for App Content

                                                                                                                                                                        To enable Google to crawl your app content and allow users to enter your app from search results, you must add intent filters for the relevant activities in your app manifest. These intent filters allow deep linking to the content in any of your activities. For example, the user might click on a deep link to view a page within a shopping app that describes a product offering that the user is searching for.

                                                                                                                                                                        Add Intent Filters for Your Deep Links

                                                                                                                                                                        To create a deep link to your app content, add an intent filter that contains these elements and attribute values in your manifest:

                                                                                                                                                                        <action>
                                                                                                                                                                        Specify the ACTION_VIEW intent action so that the intent filter can be reached from Google Search.
                                                                                                                                                                        <data>
                                                                                                                                                                        Add one or more <data> tags, where each tag represents a URI format that resolves to the activity. At minimum, the <data> tag must include the android:scheme attribute.

                                                                                                                                                                        You can add additional attributes to further refine the type of URI that the activity accepts. For example, you might have multiple activities that accept similar URIs, but which differ simply based on the path name. In this case, use the android:path attribute or its variants (pathPattern or pathPrefix) to differentiate which activity the system should open for different URI paths.

                                                                                                                                                                        <category>
                                                                                                                                                                        Include the BROWSABLE category. The BROWSABLE category is required in order for the intent filter to be accessible from a web browser. Without it, clicking a link in a browser cannot resolve to your app. The DEFAULT category is optional, but recommended. Without this category, the activity can be started only with an explicit intent, using your app component name.

                                                                                                                                                                        The following XML snippet shows how you might specify an intent filter in your manifest for deep linking. The URIs “example://gizmos” and “http://www.example.com/gizmos” both resolve to this activity.

                                                                                                                                                                        <activity
                                                                                                                                                                            android:name="com.example.android.GizmosActivity"
                                                                                                                                                                            android:label="@string/title_gizmos" >
                                                                                                                                                                            <intent-filter android:label="@string/filter_title_viewgizmos">
                                                                                                                                                                                <action android:name="android.intent.action.VIEW" />
                                                                                                                                                                                <category android:name="android.intent.category.DEFAULT" />
                                                                                                                                                                                <category android:name="android.intent.category.BROWSABLE" />
                                                                                                                                                                                <!-- Accepts URIs that begin with "http://www.example.com/gizmos” -->
                                                                                                                                                                                <data android:scheme="http"
                                                                                                                                                                                      android:host="www.example.com"
                                                                                                                                                                                      android:pathPrefix="/gizmos" />
                                                                                                                                                                                <!-- note that the leading "/" is required for pathPrefix-->
                                                                                                                                                                                <!-- Accepts URIs that begin with "example://gizmos” -->
                                                                                                                                                                                <data android:scheme="example"
                                                                                                                                                                                      android:host="gizmos" />
                                                                                                                                                                                
                                                                                                                                                                            </intent-filter>
                                                                                                                                                                        </activity>
                                                                                                                                                                        

                                                                                                                                                                        Once you've added intent filters with URIs for activity content to your app manifest, Android is able to route any Intent that has matching URIs to your app at runtime.

                                                                                                                                                                        Note: Intent filters may only contain a single data element for a URI pattern. Create separate intent filters to capture additional URI patterns.

                                                                                                                                                                        To learn more about defining intent filters, see Allow Other Apps to Start Your Activity.

                                                                                                                                                                        Read Data from Incoming Intents

                                                                                                                                                                        Once the system starts your activity through an intent filter, you can use data provided by the Intent to determine what you need to render. Call the getData() and getAction() methods to retrieve the data and action associated with the incoming Intent. You can call these methods at any time during the lifecycle of the activity, but you should generally do so during early callbacks such as onCreate() or onStart().

                                                                                                                                                                        Here’s a snippet that shows how to retrieve data from an Intent:

                                                                                                                                                                        @Override
                                                                                                                                                                        public void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                            super.onCreate(savedInstanceState);
                                                                                                                                                                            setContentView(R.layout.main);
                                                                                                                                                                        
                                                                                                                                                                            Intent intent = getIntent();
                                                                                                                                                                            String action = intent.getAction();
                                                                                                                                                                            Uri data = intent.getData();
                                                                                                                                                                        }
                                                                                                                                                                        

                                                                                                                                                                        Follow these best practices to improve the user's experience:

                                                                                                                                                                        • The deep link should take users directly to the content, without any prompts, interstitial pages, or logins. Make sure that users can see the app content even if they never previously opened the application. It is okay to prompt users on subsequent interactions or when they open the app from the Launcher. This is the same principle as the first click free experience for web sites.
                                                                                                                                                                        • Follow the design guidance described in Navigation with Back and Up so that your app matches users' expectations for backward navigation after they enter your app through a deep link.

                                                                                                                                                                        Test Your Deep Links

                                                                                                                                                                        You can use the Android Debug Bridge with the activity manager (am) tool to test that the intent filter URIs you specified for deep linking resolve to the correct app activity. You can run the adb command against a device or an emulator.

                                                                                                                                                                        The general syntax for testing an intent filter URI with adb is:

                                                                                                                                                                        $ adb shell am start
                                                                                                                                                                                -W -a android.intent.action.VIEW
                                                                                                                                                                                -d <URI> <PACKAGE>
                                                                                                                                                                        

                                                                                                                                                                        For example, the command below tries to view a target app activity that is associated with the specified URI.

                                                                                                                                                                        $ adb shell am start
                                                                                                                                                                                -W -a android.intent.action.VIEW
                                                                                                                                                                                -d "example://gizmos" com.example.android
                                                                                                                                                                        
                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                        Specifying App Content for Indexing | Android Developers Skip to content

                                                                                                                                                                        Most visited

                                                                                                                                                                        Recently visited

                                                                                                                                                                        navigation

                                                                                                                                                                          Specifying App Content for Indexing

                                                                                                                                                                          Google's web crawling bot (Googlebot), which crawls and indexes web sites for the Google search engine, can also index content in your Android app. By opting in, you can allow Googlebot to crawl the content in the APK through the Google Play Store to index the app content. To indicate which app content you’d like Google to index, simply add link elements either to your existing Sitemap file or in the <head> element of each web page in your site, in the same way as you would for web pages.

                                                                                                                                                                          The deep links that you share with Google Search must take this URI format:

                                                                                                                                                                          android-app://<package_name>/<scheme>/<host_path>
                                                                                                                                                                          

                                                                                                                                                                          The components that make up the URI format are:

                                                                                                                                                                          • package_name. Represents the package name for your APK as listed in the Google Play Developer Console.
                                                                                                                                                                          • scheme. The URI scheme that matches your intent filter.
                                                                                                                                                                          • host_path. Identifies the specific content within your application.

                                                                                                                                                                          The following sections describe how to add a deep link URI to your Sitemap or web pages.

                                                                                                                                                                          Add Deep Links in Your Sitemap

                                                                                                                                                                          To annotate the deep link for Google Search app indexing in your Sitemap, use the <xhtml:link> tag and specify the deep link as an alternate URI.

                                                                                                                                                                          For example, the following XML snippet shows how you might specify a link to your web page by using the <loc> tag, and a corresponding deep link to your Android app by using the <xhtml:link> tag.

                                                                                                                                                                          <?xml version="1.0" encoding="UTF-8" ?>
                                                                                                                                                                          <urlset
                                                                                                                                                                              xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
                                                                                                                                                                              xmlns:xhtml="http://www.w3.org/1999/xhtml">
                                                                                                                                                                              <url>
                                                                                                                                                                                  <loc>example://gizmos</loc>
                                                                                                                                                                                      <xhtml:link
                                                                                                                                                                                          rel="alternate"
                                                                                                                                                                                          href="android-app://com.example.android/example/gizmos" />
                                                                                                                                                                              </url>
                                                                                                                                                                              ...
                                                                                                                                                                          </urlset>
                                                                                                                                                                          

                                                                                                                                                                          Add Deep Links in Your Web Pages

                                                                                                                                                                          Instead of specifying the deep links for Google Search app indexing in your Sitemap file, you can annotate the deep links in the HTML markup of your web pages. You can do this in the <head> section for each web page by adding a <link> tag and specifying the deep link as an alternate URI.

                                                                                                                                                                          For example, the following HTML snippet shows how you might specify the corresponding deep link in a web page that has the URL example://gizmos.

                                                                                                                                                                          <html>
                                                                                                                                                                          <head>
                                                                                                                                                                              <link rel="alternate"
                                                                                                                                                                                    href="android-app://com.example.android/example/gizmos" />
                                                                                                                                                                              ...
                                                                                                                                                                          </head>
                                                                                                                                                                          <body> ... </body>
                                                                                                                                                                          

                                                                                                                                                                          Allow Google to Crawl URLs Requested By Your App

                                                                                                                                                                          Typically, you control how Googlebot crawls publicly accessible URLs on your site by using a robots.txt file. When Googlebot indexes your app content, your app might make HTTP requests as part of its normal operations. However, these requests will appear to your servers as originating from Googlebot. Therefore, you must configure your server's robots.txt file properly to allow these requests.

                                                                                                                                                                          For example, the following robots.txt directive shows how you might allow access to a specific directory in your web site (for example, /api/) that your app needs to access, while restricting Googlebot's access to other parts of your site.

                                                                                                                                                                          User-Agent: Googlebot
                                                                                                                                                                          Allow: /api/
                                                                                                                                                                          Disallow: /
                                                                                                                                                                          

                                                                                                                                                                          To learn more about how to modify robots.txt to control web crawling, see the Controlling Crawling and Indexing Getting Started guide.

                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                          Best Practices for User Interface | Android Developers Skip to content

                                                                                                                                                                          Most visited

                                                                                                                                                                          Recently visited

                                                                                                                                                                          navigation

                                                                                                                                                                            Best Practices for User Interface

                                                                                                                                                                            These classes teach you how to build a user interface using Android layouts for all types of devices. Android provides a flexible framework for UI design that allows your app to display different layouts for different devices, create custom UI widgets, and even control aspects of the system UI outside your app's window.

                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                            Designing for Multiple Screens | Android Developers Skip to content

                                                                                                                                                                            Most visited

                                                                                                                                                                            Recently visited

                                                                                                                                                                            navigation

                                                                                                                                                                              Designing for Multiple Screens

                                                                                                                                                                              Dependencies and prerequisites

                                                                                                                                                                              You should also read

                                                                                                                                                                              Try it out

                                                                                                                                                                              Download the sample app

                                                                                                                                                                              NewsReader.zip

                                                                                                                                                                              Android powers hundreds of device types with several different screen sizes, ranging from small phones to large TV sets. Therefore, it’s important that you design your application to be compatible with all screen sizes so it’s available to as many users as possible.

                                                                                                                                                                              But being compatible with different device types is not enough. Each screen size offers different possibilities and challenges for user interaction, so in order to truly satisfy and impress your users, your application must go beyond merely supporting multiple screens: it must optimize the user experience for each screen configuration.

                                                                                                                                                                              This class shows you how to implement a user interface that's optimized for several screen configurations.

                                                                                                                                                                              The code in each lesson comes from a sample application that demonstrates best practices in optimizing for multiple screens. You can download the sample (to the right) and use it as a source of reusable code for your own application.

                                                                                                                                                                              Note: This class and the associated sample use the support library in order to use the Fragment APIs on versions lower than Android 3.0. You must download and add the library to your application in order to use all APIs in this class.

                                                                                                                                                                              Lessons

                                                                                                                                                                              Supporting Different Screen Sizes
                                                                                                                                                                              This lesson walks you through how to design layouts that adapts several different screen sizes (using flexible dimensions for views, RelativeLayout, screen size and orientation qualifiers, alias filters, and nine-patch bitmaps).
                                                                                                                                                                              Supporting Different Screen Densities
                                                                                                                                                                              This lesson shows you how to support screens that have different pixel densities (using density-independent pixels and providing bitmaps appropriate for each density).
                                                                                                                                                                              Implementing Adaptative UI Flows
                                                                                                                                                                              This lesson shows you how to implement your UI flow in a way that adapts to several screen size/density combinations (run-time detection of active layout, reacting according to current layout, handling screen configuration changes).
                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                              Supporting Different Screen Sizes | Android Developers Skip to content

                                                                                                                                                                              Most visited

                                                                                                                                                                              Recently visited

                                                                                                                                                                              navigation

                                                                                                                                                                                Supporting Different Screen Sizes

                                                                                                                                                                                This lesson shows you how to support different screen sizes by:

                                                                                                                                                                                • Ensuring your layout can be adequately resized to fit the screen
                                                                                                                                                                                • Providing appropriate UI layout according to screen configuration
                                                                                                                                                                                • Ensuring the correct layout is applied to the correct screen
                                                                                                                                                                                • Providing bitmaps that scale correctly

                                                                                                                                                                                Use "wrap_content" and "match_parent"

                                                                                                                                                                                To ensure that your layout is flexible and adapts to different screen sizes, you should use "wrap_content" and "match_parent" for the width and height of some view components. If you use "wrap_content", the width or height of the view is set to the minimum size necessary to fit the content within that view, while "match_parent" makes the component expand to match the size of its parent view.

                                                                                                                                                                                By using the "wrap_content" and "match_parent" size values instead of hard-coded sizes, your views either use only the space required for that view or expand to fill the available space, respectively. For example:

                                                                                                                                                                                <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                    android:orientation="vertical"
                                                                                                                                                                                    android:layout_width="match_parent"
                                                                                                                                                                                    android:layout_height="match_parent">
                                                                                                                                                                                    <LinearLayout android:layout_width="match_parent" 
                                                                                                                                                                                                  android:id="@+id/linearLayout1"  
                                                                                                                                                                                                  android:gravity="center"
                                                                                                                                                                                                  android:layout_height="50dp">
                                                                                                                                                                                        <ImageView android:id="@+id/imageView1" 
                                                                                                                                                                                                   android:layout_height="wrap_content"
                                                                                                                                                                                                   android:layout_width="wrap_content"
                                                                                                                                                                                                   android:src="@drawable/logo"
                                                                                                                                                                                                   android:paddingRight="30dp"
                                                                                                                                                                                                   android:layout_gravity="left"
                                                                                                                                                                                                   android:layout_weight="0" />
                                                                                                                                                                                        <View android:layout_height="wrap_content" 
                                                                                                                                                                                              android:id="@+id/view1"
                                                                                                                                                                                              android:layout_width="wrap_content"
                                                                                                                                                                                              android:layout_weight="1" />
                                                                                                                                                                                        <Button android:id="@+id/categorybutton"
                                                                                                                                                                                                android:background="@drawable/button_bg"
                                                                                                                                                                                                android:layout_height="match_parent"
                                                                                                                                                                                                android:layout_weight="0"
                                                                                                                                                                                                android:layout_width="120dp"
                                                                                                                                                                                                style="@style/CategoryButtonStyle"/>
                                                                                                                                                                                    </LinearLayout>
                                                                                                                                                                                
                                                                                                                                                                                    <fragment android:id="@+id/headlines" 
                                                                                                                                                                                              android:layout_height="fill_parent"
                                                                                                                                                                                              android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                              android:layout_width="match_parent" />
                                                                                                                                                                                </LinearLayout>

                                                                                                                                                                                Notice how the sample uses "wrap_content" and "match_parent" for component sizes rather than specific dimensions. This allows the layout to adapt correctly to different screen sizes and orientations.

                                                                                                                                                                                For example, this is what this layout looks like in portrait and landscape mode. Notice that the sizes of the components adapt automatically to the width and height:

                                                                                                                                                                                Figure 1. The News Reader sample app in portrait (left) and landscape (right).

                                                                                                                                                                                Use RelativeLayout

                                                                                                                                                                                You can construct fairly complex layouts using nested instances of LinearLayout and combinations of "wrap_content" and "match_parent" sizes. However, LinearLayout does not allow you to precisely control the spacial relationships of child views; views in a LinearLayout simply line up side-by-side. If you need child views to be oriented in variations other than a straight line, a better solution is often to use a RelativeLayout, which allows you to specify your layout in terms of the spacial relationships between components. For instance, you can align one child view on the left side and another view on the right side of the screen.

                                                                                                                                                                                For example:

                                                                                                                                                                                <?xml version="1.0" encoding="utf-8"?>
                                                                                                                                                                                <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                    android:layout_width="match_parent"
                                                                                                                                                                                    android:layout_height="match_parent">
                                                                                                                                                                                    <TextView
                                                                                                                                                                                        android:id="@+id/label"
                                                                                                                                                                                        android:layout_width="match_parent"
                                                                                                                                                                                        android:layout_height="wrap_content"
                                                                                                                                                                                        android:text="Type here:"/>
                                                                                                                                                                                    <EditText
                                                                                                                                                                                        android:id="@+id/entry"
                                                                                                                                                                                        android:layout_width="match_parent"
                                                                                                                                                                                        android:layout_height="wrap_content"
                                                                                                                                                                                        android:layout_below="@id/label"/>
                                                                                                                                                                                    <Button
                                                                                                                                                                                        android:id="@+id/ok"
                                                                                                                                                                                        android:layout_width="wrap_content"
                                                                                                                                                                                        android:layout_height="wrap_content"
                                                                                                                                                                                        android:layout_below="@id/entry"
                                                                                                                                                                                        android:layout_alignParentRight="true"
                                                                                                                                                                                        android:layout_marginLeft="10dp"
                                                                                                                                                                                        android:text="OK" />
                                                                                                                                                                                    <Button
                                                                                                                                                                                        android:layout_width="wrap_content"
                                                                                                                                                                                        android:layout_height="wrap_content"
                                                                                                                                                                                        android:layout_toLeftOf="@id/ok"
                                                                                                                                                                                        android:layout_alignTop="@id/ok"
                                                                                                                                                                                        android:text="Cancel" />
                                                                                                                                                                                </RelativeLayout>
                                                                                                                                                                                

                                                                                                                                                                                Figure 2 shows how this layout appears on a QVGA screen.

                                                                                                                                                                                Figure 2. Screenshot on a QVGA screen (small screen).

                                                                                                                                                                                Figure 3 shows how it appears on a larger screen.

                                                                                                                                                                                Figure 3. Screenshot on a WSVGA screen (large screen).

                                                                                                                                                                                Notice that although the size of the components changed, their spatial relationships are preserved as specified by the RelativeLayout.LayoutParams.

                                                                                                                                                                                Use Size Qualifiers

                                                                                                                                                                                There's only so much mileage you can get from a flexible layout or relative layout like the one in the previous sections. While those layouts adapt to different screens by stretching the space within and around components, they may not provide the best user experience for each screen size. Therefore, your application should not only implement flexible layouts, but should also provide several alternative layouts to target different screen configurations. You do so by using configuration qualifiers, which allows the runtime to automatically select the appropriate resource based on the current device’s configuration (such as a different layout design for different screen sizes).

                                                                                                                                                                                For example, many applications implement the "two pane" pattern for large screens (the app might show a list of items on one pane and the content on another pane). Tablets and TVs are large enough for both panes to fit simultaneously on screen, but phone screens have to show them separately. So, to implement these layouts, you could have the following files:

                                                                                                                                                                                • res/layout/main.xml, single-pane (default) layout:
                                                                                                                                                                                  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                      android:orientation="vertical"
                                                                                                                                                                                      android:layout_width="match_parent"
                                                                                                                                                                                      android:layout_height="match_parent">
                                                                                                                                                                                  
                                                                                                                                                                                      <fragment android:id="@+id/headlines"
                                                                                                                                                                                                android:layout_height="fill_parent"
                                                                                                                                                                                                android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                                android:layout_width="match_parent" />
                                                                                                                                                                                  </LinearLayout>
                                                                                                                                                                                • res/layout-large/main.xml, two-pane layout:
                                                                                                                                                                                  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                      android:layout_width="fill_parent"
                                                                                                                                                                                      android:layout_height="fill_parent"
                                                                                                                                                                                      android:orientation="horizontal">
                                                                                                                                                                                      <fragment android:id="@+id/headlines"
                                                                                                                                                                                                android:layout_height="fill_parent"
                                                                                                                                                                                                android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                                android:layout_width="400dp"
                                                                                                                                                                                                android:layout_marginRight="10dp"/>
                                                                                                                                                                                      <fragment android:id="@+id/article"
                                                                                                                                                                                                android:layout_height="fill_parent"
                                                                                                                                                                                                android:name="com.example.android.newsreader.ArticleFragment"
                                                                                                                                                                                                android:layout_width="fill_parent" />
                                                                                                                                                                                  </LinearLayout>

                                                                                                                                                                                Notice the large qualifier in the directory name of the second layout. This layout will be selected on devices with screens classified as large (for example, 7" tablets and above). The other layout (without qualifiers) will be selected for smaller devices.

                                                                                                                                                                                Use the Smallest-width Qualifier

                                                                                                                                                                                One of the difficulties developers had in pre-3.2 Android devices was the "large" screen size bin, which encompasses the Dell Streak, the original Galaxy Tab, and 7" tablets in general. However, many applications may want to show different layouts for different devices in this category (such as for 5" and 7" devices), even though they are all considered to be "large" screens. That's why Android introduced the "Smallest-width" qualifier (amongst others) in Android 3.2.

                                                                                                                                                                                The Smallest-width qualifier allows you to target screens that have a certain minimum width given in dp. For example, the typical 7" tablet has a minimum width of 600 dp, so if you want your UI to have two panes on those screens (but a single list on smaller screens), you can use the same two layouts from the previous section for single and two-pane layouts, but instead of the large size qualifier, use sw600dp to indicate the two-pane layout is for screens on which the smallest-width is 600 dp:

                                                                                                                                                                                • res/layout/main.xml, single-pane (default) layout:
                                                                                                                                                                                  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                      android:orientation="vertical"
                                                                                                                                                                                      android:layout_width="match_parent"
                                                                                                                                                                                      android:layout_height="match_parent">
                                                                                                                                                                                  
                                                                                                                                                                                      <fragment android:id="@+id/headlines"
                                                                                                                                                                                                android:layout_height="fill_parent"
                                                                                                                                                                                                android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                                android:layout_width="match_parent" />
                                                                                                                                                                                  </LinearLayout>
                                                                                                                                                                                • res/layout-sw600dp/main.xml, two-pane layout:
                                                                                                                                                                                  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                      android:layout_width="fill_parent"
                                                                                                                                                                                      android:layout_height="fill_parent"
                                                                                                                                                                                      android:orientation="horizontal">
                                                                                                                                                                                      <fragment android:id="@+id/headlines"
                                                                                                                                                                                                android:layout_height="fill_parent"
                                                                                                                                                                                                android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                                android:layout_width="400dp"
                                                                                                                                                                                                android:layout_marginRight="10dp"/>
                                                                                                                                                                                      <fragment android:id="@+id/article"
                                                                                                                                                                                                android:layout_height="fill_parent"
                                                                                                                                                                                                android:name="com.example.android.newsreader.ArticleFragment"
                                                                                                                                                                                                android:layout_width="fill_parent" />
                                                                                                                                                                                  </LinearLayout>

                                                                                                                                                                                This means that devices whose smallest width is greater than or equal to 600dp will select the layout-sw600dp/main.xml (two-pane) layout, while smaller screens will select the layout/main.xml (single-pane) layout.

                                                                                                                                                                                However, this won't work well on pre-3.2 devices, because they don't recognize sw600dp as a size qualifier, so you still have to use the large qualifier as well. So, you should have a file named res/layout-large/main.xml which is identical to res/layout-sw600dp/main.xml. In the next section you'll see a technique that allows you to avoid duplicating the layout files this way.

                                                                                                                                                                                Use Layout Aliases

                                                                                                                                                                                The smallest-width qualifier is available only on Android 3.2 and above. Therefore, you should also still use the abstract size bins (small, normal, large and xlarge) to be compatible with earlier versions. For example, if you want to design your UI so that it shows a single-pane UI on phones but a multi-pane UI on 7" tablets, TVs and other large devices, you'd have to supply these files:

                                                                                                                                                                                • res/layout/main.xml: single-pane layout
                                                                                                                                                                                • res/layout-large: multi-pane layout
                                                                                                                                                                                • res/layout-sw600dp: multi-pane layout

                                                                                                                                                                                The last two files are identical, because one of them will be matched by Android 3.2 devices, and the other one is for the benefit of tablets and TVs with earlier versions of Android.

                                                                                                                                                                                To avoid this duplication of the same file for tablets and TVs (and the maintenance headache resulting from it), you can use alias files. For example, you can define the following layouts:

                                                                                                                                                                                • res/layout/main.xml, single-pane layout
                                                                                                                                                                                • res/layout/main_twopanes.xml, two-pane layout

                                                                                                                                                                                And add these two files:

                                                                                                                                                                                • res/values-large/layout.xml:
                                                                                                                                                                                  <resources>
                                                                                                                                                                                      <item name="main" type="layout">@layout/main_twopanes</item>
                                                                                                                                                                                  </resources>
                                                                                                                                                                                  
                                                                                                                                                                                • res/values-sw600dp/layout.xml:
                                                                                                                                                                                  <resources>
                                                                                                                                                                                      <item name="main" type="layout">@layout/main_twopanes</item>
                                                                                                                                                                                  </resources>
                                                                                                                                                                                  

                                                                                                                                                                                These latter two files have identical content, but they don’t actually define the layout. They merely set up main to be an alias to main_twopanes. Since these files have large and sw600dp selectors, they are applied to tablets and TVs regardless of Android version (pre-3.2 tablets and TVs match large, and post-3.2 will match sw600dp).

                                                                                                                                                                                Use Orientation Qualifiers

                                                                                                                                                                                Some layouts work well in both landscape and portrait orientations, but most of them can benefit from adjustments. In the News Reader sample app, here is how the layout behaves in each screen size and orientation:

                                                                                                                                                                                • small screen, portrait: single pane, with logo
                                                                                                                                                                                • small screen, landscape: single pane, with logo
                                                                                                                                                                                • 7" tablet, portrait: single pane, with action bar
                                                                                                                                                                                • 7" tablet, landscape: dual pane, wide, with action bar
                                                                                                                                                                                • 10" tablet, portrait: dual pane, narrow, with action bar
                                                                                                                                                                                • 10" tablet, landscape: dual pane, wide, with action bar
                                                                                                                                                                                • TV, landscape: dual pane, wide, with action bar

                                                                                                                                                                                So each of these layouts is defined in an XML file in the res/layout/ directory. To then assign each layout to the various screen configurations, the app uses layout aliases to match them to each configuration:

                                                                                                                                                                                res/layout/onepane.xml:

                                                                                                                                                                                <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                    android:orientation="vertical"
                                                                                                                                                                                    android:layout_width="match_parent"
                                                                                                                                                                                    android:layout_height="match_parent">
                                                                                                                                                                                
                                                                                                                                                                                    <fragment android:id="@+id/headlines"
                                                                                                                                                                                              android:layout_height="fill_parent"
                                                                                                                                                                                              android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                              android:layout_width="match_parent" />
                                                                                                                                                                                </LinearLayout>

                                                                                                                                                                                res/layout/onepane_with_bar.xml:

                                                                                                                                                                                <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                    android:orientation="vertical"
                                                                                                                                                                                    android:layout_width="match_parent"
                                                                                                                                                                                    android:layout_height="match_parent">
                                                                                                                                                                                    <LinearLayout android:layout_width="match_parent" 
                                                                                                                                                                                                  android:id="@+id/linearLayout1"  
                                                                                                                                                                                                  android:gravity="center"
                                                                                                                                                                                                  android:layout_height="50dp">
                                                                                                                                                                                        <ImageView android:id="@+id/imageView1" 
                                                                                                                                                                                                   android:layout_height="wrap_content"
                                                                                                                                                                                                   android:layout_width="wrap_content"
                                                                                                                                                                                                   android:src="@drawable/logo"
                                                                                                                                                                                                   android:paddingRight="30dp"
                                                                                                                                                                                                   android:layout_gravity="left"
                                                                                                                                                                                                   android:layout_weight="0" />
                                                                                                                                                                                        <View android:layout_height="wrap_content" 
                                                                                                                                                                                              android:id="@+id/view1"
                                                                                                                                                                                              android:layout_width="wrap_content"
                                                                                                                                                                                              android:layout_weight="1" />
                                                                                                                                                                                        <Button android:id="@+id/categorybutton"
                                                                                                                                                                                                android:background="@drawable/button_bg"
                                                                                                                                                                                                android:layout_height="match_parent"
                                                                                                                                                                                                android:layout_weight="0"
                                                                                                                                                                                                android:layout_width="120dp"
                                                                                                                                                                                                style="@style/CategoryButtonStyle"/>
                                                                                                                                                                                    </LinearLayout>
                                                                                                                                                                                
                                                                                                                                                                                    <fragment android:id="@+id/headlines" 
                                                                                                                                                                                              android:layout_height="fill_parent"
                                                                                                                                                                                              android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                              android:layout_width="match_parent" />
                                                                                                                                                                                </LinearLayout>

                                                                                                                                                                                res/layout/twopanes.xml:

                                                                                                                                                                                <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                    android:layout_width="fill_parent"
                                                                                                                                                                                    android:layout_height="fill_parent"
                                                                                                                                                                                    android:orientation="horizontal">
                                                                                                                                                                                    <fragment android:id="@+id/headlines"
                                                                                                                                                                                              android:layout_height="fill_parent"
                                                                                                                                                                                              android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                              android:layout_width="400dp"
                                                                                                                                                                                              android:layout_marginRight="10dp"/>
                                                                                                                                                                                    <fragment android:id="@+id/article"
                                                                                                                                                                                              android:layout_height="fill_parent"
                                                                                                                                                                                              android:name="com.example.android.newsreader.ArticleFragment"
                                                                                                                                                                                              android:layout_width="fill_parent" />
                                                                                                                                                                                </LinearLayout>

                                                                                                                                                                                res/layout/twopanes_narrow.xml:

                                                                                                                                                                                <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                    android:layout_width="fill_parent"
                                                                                                                                                                                    android:layout_height="fill_parent"
                                                                                                                                                                                    android:orientation="horizontal">
                                                                                                                                                                                    <fragment android:id="@+id/headlines"
                                                                                                                                                                                              android:layout_height="fill_parent"
                                                                                                                                                                                              android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                              android:layout_width="200dp"
                                                                                                                                                                                              android:layout_marginRight="10dp"/>
                                                                                                                                                                                    <fragment android:id="@+id/article"
                                                                                                                                                                                              android:layout_height="fill_parent"
                                                                                                                                                                                              android:name="com.example.android.newsreader.ArticleFragment"
                                                                                                                                                                                              android:layout_width="fill_parent" />
                                                                                                                                                                                </LinearLayout>

                                                                                                                                                                                Now that all possible layouts are defined, it's just a matter of mapping the correct layout to each configuration using the configuration qualifiers. You can now do it using the layout alias technique:

                                                                                                                                                                                res/values/layouts.xml:

                                                                                                                                                                                <resources>
                                                                                                                                                                                    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>
                                                                                                                                                                                    <bool name="has_two_panes">false</bool>
                                                                                                                                                                                </resources>

                                                                                                                                                                                res/values-sw600dp-land/layouts.xml:

                                                                                                                                                                                <resources>
                                                                                                                                                                                    <item name="main_layout" type="layout">@layout/twopanes</item>
                                                                                                                                                                                    <bool name="has_two_panes">true</bool>
                                                                                                                                                                                </resources>

                                                                                                                                                                                res/values-sw600dp-port/layouts.xml:

                                                                                                                                                                                <resources>
                                                                                                                                                                                    <item name="main_layout" type="layout">@layout/onepane</item>
                                                                                                                                                                                    <bool name="has_two_panes">false</bool>
                                                                                                                                                                                </resources>

                                                                                                                                                                                res/values-large-land/layouts.xml:

                                                                                                                                                                                <resources>
                                                                                                                                                                                    <item name="main_layout" type="layout">@layout/twopanes</item>
                                                                                                                                                                                    <bool name="has_two_panes">true</bool>
                                                                                                                                                                                </resources>

                                                                                                                                                                                res/values-large-port/layouts.xml:

                                                                                                                                                                                <resources>
                                                                                                                                                                                    <item name="main_layout" type="layout">@layout/twopanes_narrow</item>
                                                                                                                                                                                    <bool name="has_two_panes">true</bool>
                                                                                                                                                                                </resources>

                                                                                                                                                                                Use Nine-patch Bitmaps

                                                                                                                                                                                Supporting different screen sizes usually means that your image resources must also be capable of adapting to different sizes. For example, a button background must fit whichever button shape it is applied to.

                                                                                                                                                                                If you use simple images on components that can change size, you will quickly notice that the results are somewhat less than impressive, since the runtime will stretch or shrink your images uniformly. The solution is using nine-patch bitmaps, which are specially formatted PNG files that indicate which areas can and cannot be stretched.

                                                                                                                                                                                Therefore, when designing bitmaps that will be used on components with variable size, always use nine-patches. To convert a bitmap into a nine-patch, you can start with a regular image (figure 4, shown with in 4x zoom for clarity).

                                                                                                                                                                                Figure 4. button.png

                                                                                                                                                                                And then run it through the draw9patch utility of the SDK (which is located in the tools/ directory), in which you can mark the areas that should be stretched by drawing pixels along the left and top borders. You can also mark the area that should hold the content by drawing pixels along the right and bottom borders, resulting in figure 5.

                                                                                                                                                                                Figure 5. button.9.png

                                                                                                                                                                                Notice the black pixels along the borders. The ones on the top and left borders indicate the places where the image can be stretched, and the ones on the right and bottom borders indicate where the content should be placed.

                                                                                                                                                                                Also, notice the .9.png extension. You must use this extension, since this is how the framework detects that this is a nine-patch image, as opposed to a regular PNG image.

                                                                                                                                                                                When you apply this background to a component (by setting android:background="@drawable/button"), the framework stretches the image correctly to accommodate the size of the button, as shown in various sizes in figure 6.

                                                                                                                                                                                Figure 6. A button using the button.9.png nine-patch in various sizes.

                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                Supporting Different Densities | Android Developers Skip to content

                                                                                                                                                                                Most visited

                                                                                                                                                                                Recently visited

                                                                                                                                                                                navigation

                                                                                                                                                                                  Supporting Different Densities

                                                                                                                                                                                  This lesson shows you how to support different screen densities by providing different resources and using resolution-independent units of measurements.

                                                                                                                                                                                  Use Density-independent Pixels

                                                                                                                                                                                  One common pitfall you must avoid when designing your layouts is using absolute pixels to define distances or sizes. Defining layout dimensions with pixels is a problem because different screens have different pixel densities, so the same number of pixels may correspond to different physical sizes on different devices. Therefore, when specifying dimensions, always use either dp or sp units. A dp is a density-independent pixel that corresponds to the physical size of a pixel at 160 dpi. An sp is the same base unit, but is scaled by the user's preferred text size (it’s a scale-independent pixel), so you should use this measurement unit when defining text size (but never for layout sizes).

                                                                                                                                                                                  Video

                                                                                                                                                                                  DesignBytes: Density-independent Pixels


                                                                                                                                                                                  For example, when you specify spacing between two views, use dp rather than px:

                                                                                                                                                                                  <Button android:layout_width="wrap_content"
                                                                                                                                                                                      android:layout_height="wrap_content"
                                                                                                                                                                                      android:text="@string/clickme"
                                                                                                                                                                                      android:layout_marginTop="20dp" />
                                                                                                                                                                                  

                                                                                                                                                                                  When specifying text size, always use sp:

                                                                                                                                                                                  <TextView android:layout_width="match_parent"
                                                                                                                                                                                      android:layout_height="wrap_content"
                                                                                                                                                                                      android:textSize="20sp" />
                                                                                                                                                                                  

                                                                                                                                                                                  Provide Alternative Bitmaps

                                                                                                                                                                                  Since Android runs in devices with a wide variety of screen densities, you should always provide your bitmap resources tailored to each of the generalized density buckets: low, medium, high and extra-high density. This will help you achieve good graphical quality and performance on all screen densities.

                                                                                                                                                                                  To generate these images, you should start with your raw resource in vector format and generate the images for each density using the following size scale:

                                                                                                                                                                                  • xhdpi: 2.0
                                                                                                                                                                                  • hdpi: 1.5
                                                                                                                                                                                  • mdpi: 1.0 (baseline)
                                                                                                                                                                                  • ldpi: 0.75

                                                                                                                                                                                  This means that if you generate a 200x200 image for xhdpi devices, you should generate the same resource in 150x150 for hdpi, 100x100 for mdpi and finally a 75x75 image for ldpi devices.

                                                                                                                                                                                  Then, place the generated image files in the appropriate subdirectory under res/ and the system will pick the correct one automatically based on the screen density of the device your application is running on:

                                                                                                                                                                                  MyProject/
                                                                                                                                                                                    res/
                                                                                                                                                                                      drawable-xhdpi/
                                                                                                                                                                                          awesomeimage.png
                                                                                                                                                                                      drawable-hdpi/
                                                                                                                                                                                          awesomeimage.png
                                                                                                                                                                                      drawable-mdpi/
                                                                                                                                                                                          awesomeimage.png
                                                                                                                                                                                      drawable-ldpi/
                                                                                                                                                                                          awesomeimage.png
                                                                                                                                                                                  

                                                                                                                                                                                  Then, any time you reference @drawable/awesomeimage, the system selects the appropriate bitmap based on the screen's dpi.

                                                                                                                                                                                  Place your launcher icons in the mipmap/ folders.

                                                                                                                                                                                  res/...
                                                                                                                                                                                      mipmap-ldpi/...
                                                                                                                                                                                          finished_launcher_asset.png
                                                                                                                                                                                      mipmap-mdpi/...
                                                                                                                                                                                          finished_launcher_asset.png
                                                                                                                                                                                      mipmap-hdpi/...
                                                                                                                                                                                          finished_launcher_asset.png
                                                                                                                                                                                      mipmap-xhdpi/...
                                                                                                                                                                                          finished_launcher_asset.png
                                                                                                                                                                                      mipmap-xxhdpi/...
                                                                                                                                                                                          finished_launcher_asset.png
                                                                                                                                                                                      mipmap-xxxhdpi/...
                                                                                                                                                                                          finished_launcher_asset.png
                                                                                                                                                                                  

                                                                                                                                                                                  Note: You should place all launcher icons in the res/mipmap-[density]/ folders, rather than drawable/ folders to ensure launcher apps use the best resolution icon. For more information about using the mipmap folders, see Managing Projects Overview.

                                                                                                                                                                                  For more tips and guidelines for creating icon assets for your application, see the Icon Design Guidelines.

                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                  Implementing Adaptative UI Flows | Android Developers Skip to content

                                                                                                                                                                                  Most visited

                                                                                                                                                                                  Recently visited

                                                                                                                                                                                  navigation

                                                                                                                                                                                    Implementing Adaptative UI Flows

                                                                                                                                                                                    Depending on the layout that your application is currently showing, the UI flow may be different. For example, if your application is in the dual-pane mode, clicking on an item on the left pane will simply display the content on the right pane; if it is in single-pane mode, the content should be displayed on its own (in a different activity).

                                                                                                                                                                                    Determine the Current Layout

                                                                                                                                                                                    Since your implementation of each layout will be a little different, one of the first things you will probably have to do is determine what layout the user is currently viewing. For example, you might want to know whether the user is in "single pane" mode or "dual pane" mode. You can do that by querying if a given view exists and is visible:

                                                                                                                                                                                    public class NewsReaderActivity extends FragmentActivity {
                                                                                                                                                                                        boolean mIsDualPane;
                                                                                                                                                                                    
                                                                                                                                                                                        @Override
                                                                                                                                                                                        public void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                                            super.onCreate(savedInstanceState);
                                                                                                                                                                                            setContentView(R.layout.main_layout);
                                                                                                                                                                                    
                                                                                                                                                                                            View articleView = findViewById(R.id.article);
                                                                                                                                                                                            mIsDualPane = articleView != null && 
                                                                                                                                                                                                            articleView.getVisibility() == View.VISIBLE;
                                                                                                                                                                                        }
                                                                                                                                                                                    }
                                                                                                                                                                                    

                                                                                                                                                                                    Notice that this code queries whether the "article" pane is available or not, which is much more flexible than hard-coding a query for a specific layout.

                                                                                                                                                                                    Another example of how you can adapt to the existence of different components is to check whether they are available before performing an operation on them. For example, in the News Reader sample app, there is a button that opens a menu, but that button only exists when running on versions older than Android 3.0 (because it's function is taken over by the ActionBar on API level 11+). So, to add the event listener for this button, you can do:

                                                                                                                                                                                    Button catButton = (Button) findViewById(R.id.categorybutton);
                                                                                                                                                                                    OnClickListener listener = /* create your listener here */;
                                                                                                                                                                                    if (catButton != null) {
                                                                                                                                                                                        catButton.setOnClickListener(listener);
                                                                                                                                                                                    }
                                                                                                                                                                                    

                                                                                                                                                                                    React According to Current Layout

                                                                                                                                                                                    Some actions may have a different result depending on the current layout. For example, in the News Reader sample, clicking on a headline from the headlines list opens the article in the right hand-side pane if the UI is in dual pane mode, but will launch a separate activity if the UI is in single-pane mode:

                                                                                                                                                                                    @Override
                                                                                                                                                                                    public void onHeadlineSelected(int index) {
                                                                                                                                                                                        mArtIndex = index;
                                                                                                                                                                                        if (mIsDualPane) {
                                                                                                                                                                                            /* display article on the right pane */
                                                                                                                                                                                            mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
                                                                                                                                                                                        } else {
                                                                                                                                                                                            /* start a separate activity */
                                                                                                                                                                                            Intent intent = new Intent(this, ArticleActivity.class);
                                                                                                                                                                                            intent.putExtra("catIndex", mCatIndex);
                                                                                                                                                                                            intent.putExtra("artIndex", index);
                                                                                                                                                                                            startActivity(intent);
                                                                                                                                                                                        }
                                                                                                                                                                                    }
                                                                                                                                                                                    

                                                                                                                                                                                    Likewise, if the app is in dual-pane mode, it should set up the action bar with tabs for navigation, whereas if the app is in single-pane mode, it should set up navigation with a spinner widget. So your code should also check which case is appropriate:

                                                                                                                                                                                    final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" };
                                                                                                                                                                                    
                                                                                                                                                                                    public void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                                        ....
                                                                                                                                                                                        if (mIsDualPane) {
                                                                                                                                                                                            /* use tabs for navigation */
                                                                                                                                                                                            actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
                                                                                                                                                                                            int i;
                                                                                                                                                                                            for (i = 0; i < CATEGORIES.length; i++) {
                                                                                                                                                                                                actionBar.addTab(actionBar.newTab().setText(
                                                                                                                                                                                                    CATEGORIES[i]).setTabListener(handler));
                                                                                                                                                                                            }
                                                                                                                                                                                            actionBar.setSelectedNavigationItem(selTab);
                                                                                                                                                                                        }
                                                                                                                                                                                        else {
                                                                                                                                                                                            /* use list navigation (spinner) */
                                                                                                                                                                                            actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
                                                                                                                                                                                            SpinnerAdapter adap = new ArrayAdapter(this, 
                                                                                                                                                                                                    R.layout.headline_item, CATEGORIES);
                                                                                                                                                                                            actionBar.setListNavigationCallbacks(adap, handler);
                                                                                                                                                                                        }
                                                                                                                                                                                    }
                                                                                                                                                                                    

                                                                                                                                                                                    Reuse Fragments in Other Activities

                                                                                                                                                                                    A recurring pattern in designing for multiple screens is having a portion of your interface that's implemented as a pane on some screen configurations and as a separate activity on other configurations. For example, in the News Reader sample, the news article text is presented in the right side pane on large screens, but is a separate activity on smaller screens.

                                                                                                                                                                                    In cases like this, you can usually avoid code duplication by reusing the same Fragment subclass in several activities. For example, ArticleFragment is used in the dual-pane layout:

                                                                                                                                                                                    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                        android:layout_width="fill_parent"
                                                                                                                                                                                        android:layout_height="fill_parent"
                                                                                                                                                                                        android:orientation="horizontal">
                                                                                                                                                                                        <fragment android:id="@+id/headlines"
                                                                                                                                                                                                  android:layout_height="fill_parent"
                                                                                                                                                                                                  android:name="com.example.android.newsreader.HeadlinesFragment"
                                                                                                                                                                                                  android:layout_width="400dp"
                                                                                                                                                                                                  android:layout_marginRight="10dp"/>
                                                                                                                                                                                        <fragment android:id="@+id/article"
                                                                                                                                                                                                  android:layout_height="fill_parent"
                                                                                                                                                                                                  android:name="com.example.android.newsreader.ArticleFragment"
                                                                                                                                                                                                  android:layout_width="fill_parent" />
                                                                                                                                                                                    </LinearLayout>

                                                                                                                                                                                    And reused (without a layout) in the activity layout for smaller screens (ArticleActivity):

                                                                                                                                                                                    ArticleFragment frag = new ArticleFragment();
                                                                                                                                                                                    getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();
                                                                                                                                                                                    

                                                                                                                                                                                    Naturally, this has the same effect as declaring the fragment in an XML layout, but in this case an XML layout is unnecessary work because the article fragment is the only component of this activity.

                                                                                                                                                                                    One very important point to keep in mind when designing your fragments is to not create a strong coupling to a specific activity. You can usually do that by defining an interface that abstracts all the ways in which the fragment needs to interact with its host activity, and then the host activity implements that interface:

                                                                                                                                                                                    For example, the News Reader app's HeadlinesFragment does precisely that:

                                                                                                                                                                                    public class HeadlinesFragment extends ListFragment {
                                                                                                                                                                                        ...
                                                                                                                                                                                        OnHeadlineSelectedListener mHeadlineSelectedListener = null;
                                                                                                                                                                                    
                                                                                                                                                                                        /* Must be implemented by host activity */
                                                                                                                                                                                        public interface OnHeadlineSelectedListener {
                                                                                                                                                                                            public void onHeadlineSelected(int index);
                                                                                                                                                                                        }
                                                                                                                                                                                        ...
                                                                                                                                                                                    
                                                                                                                                                                                        public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
                                                                                                                                                                                            mHeadlineSelectedListener = listener;
                                                                                                                                                                                        }
                                                                                                                                                                                    }
                                                                                                                                                                                    

                                                                                                                                                                                    Then, when the user selects a headline, the fragment notifies the listener specified by the host activity (as opposed to notifying a specific hard-coded activity):

                                                                                                                                                                                    public class HeadlinesFragment extends ListFragment {
                                                                                                                                                                                        ...
                                                                                                                                                                                        @Override
                                                                                                                                                                                        public void onItemClick(AdapterView<?> parent, 
                                                                                                                                                                                                                View view, int position, long id) {
                                                                                                                                                                                            if (null != mHeadlineSelectedListener) {
                                                                                                                                                                                                mHeadlineSelectedListener.onHeadlineSelected(position);
                                                                                                                                                                                            }
                                                                                                                                                                                        }
                                                                                                                                                                                        ...
                                                                                                                                                                                    }
                                                                                                                                                                                    

                                                                                                                                                                                    This technique is discussed further in the guide to Supporting Tablets and Handsets.

                                                                                                                                                                                    Handle Screen Configuration Changes

                                                                                                                                                                                    If you are using separate activities to implement separate parts of your interface, you have to keep in mind that it may be necessary to react to certain configuration changes (such as a rotation change) in order to keep your interface consistent.

                                                                                                                                                                                    For example, on a typical 7" tablet running Android 3.0 or higher, the News Reader sample uses a separate activity to display the news article when running in portrait mode, but uses a two-pane layout when in landscape mode.

                                                                                                                                                                                    This means that when the user is in portrait mode and the activity for viewing an article is onscreen, you need to detect that the orientation changed to landscape and react appropriately by ending the activity and return to the main activity so the content can display in the two-pane layout:

                                                                                                                                                                                    public class ArticleActivity extends FragmentActivity {
                                                                                                                                                                                        int mCatIndex, mArtIndex;
                                                                                                                                                                                    
                                                                                                                                                                                        @Override
                                                                                                                                                                                        protected void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                                            super.onCreate(savedInstanceState);
                                                                                                                                                                                            mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
                                                                                                                                                                                            mArtIndex = getIntent().getExtras().getInt("artIndex", 0);
                                                                                                                                                                                    
                                                                                                                                                                                            // If should be in two-pane mode, finish to return to main activity
                                                                                                                                                                                            if (getResources().getBoolean(R.bool.has_two_panes)) {
                                                                                                                                                                                                finish();
                                                                                                                                                                                                return;
                                                                                                                                                                                            }
                                                                                                                                                                                            ...
                                                                                                                                                                                    }
                                                                                                                                                                                    
                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                    Adding the App Bar | Android Developers Skip to content

                                                                                                                                                                                    Most visited

                                                                                                                                                                                    Recently visited

                                                                                                                                                                                    navigation

                                                                                                                                                                                      Adding the App Bar

                                                                                                                                                                                      The app bar, also known as the action bar, is one of the most important design elements in your app's activities, because it provides a visual structure and interactive elements that are familiar to users. Using the app bar makes your app consistent with other Android apps, allowing users to quickly understand how to operate your app and have a great experience. The key functions of the app bar are as follows:

                                                                                                                                                                                      • A dedicated space for giving your app an identity and indicating the user's location in the app.
                                                                                                                                                                                      • Access to important actions in a predictable way, such as search.
                                                                                                                                                                                      • Support for navigation and view switching (with tabs or drop-down lists).

                                                                                                                                                                                      This class describes how to use the v7 appcompat support library's Toolbar widget as an app bar. There are other ways to implement an app bar—for example, some themes set up an ActionBar as an app bar by default—but using the appcompat Toolbar makes it easy to set up an app bar that works on the widest range of devices, and also gives you room to customize your app bar later on as your app develops.

                                                                                                                                                                                      Lessons

                                                                                                                                                                                      Setting Up the App Bar
                                                                                                                                                                                      Learn how to add a Toolbar widget to your activity, and set it as the activity's app bar.
                                                                                                                                                                                      Adding and Handling Actions
                                                                                                                                                                                      Learn how to add actions to the app bar and its overflow menu, and how to respond when users choose those actions.
                                                                                                                                                                                      Adding an Up Action
                                                                                                                                                                                      Learn how to add an Up button to your app bar, so users can navigate back to the app's home screen.
                                                                                                                                                                                      Action Views and Action Providers
                                                                                                                                                                                      Learn how to use these widgets to provide advanced functionality in your app bar.
                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                      Setting Up the App Bar | Android Developers Skip to content

                                                                                                                                                                                      Most visited

                                                                                                                                                                                      Recently visited

                                                                                                                                                                                      navigation

                                                                                                                                                                                        Setting Up the App Bar

                                                                                                                                                                                        In its most basic form, the action bar displays the title for the activity on one side and an overflow menu on the other. Even in this simple form, the app bar provides useful information to the users, and helps to give Android apps a consistent look and feel.

                                                                                                                                                                                        Figure 1. An app bar with the app title and overflow menu.

                                                                                                                                                                                        Beginning with Android 3.0 (API level 11), all activities that use the default theme have an ActionBar as an app bar. However, app bar features have gradually been added to the native ActionBar over various Android releases. As a result, the native ActionBar behaves differently depending on what version of the Android system a device may be using. By contrast, the most recent features are added to the support library's version of Toolbar, and they are available on any device that can use the support library.

                                                                                                                                                                                        For this reason, you should use the support library's Toolbar class to implement your activities' app bars. Using the support library's toolbar helps ensure that your app will have consistent behavior across the widest range of devices. For example, the Toolbar widget provides a material design experience on devices running Android 2.1 (API level 7) or later, but the native action bar doesn't support material design unless the device is running Android 5.0 (API level 21) or later.

                                                                                                                                                                                        Add a Toolbar to an Activity

                                                                                                                                                                                        These steps describe how to set up a Toolbar as your activity's app bar:
                                                                                                                                                                                        1. Add the v7 appcompat support library to your project, as described in Support Library Setup.
                                                                                                                                                                                        2. Make sure the activity extends AppCompatActivity:
                                                                                                                                                                                          public class MyActivity extends AppCompatActivity {
                                                                                                                                                                                            // ...
                                                                                                                                                                                          }
                                                                                                                                                                                          

                                                                                                                                                                                          Note: Make this change for every activity in your app that uses a Toolbar as an app bar.

                                                                                                                                                                                        3. In the app manifest, set the <application> element to use one of appcompat's NoActionBar themes. Using one of these themes prevents the app from using the native ActionBar class to provide the app bar. For example:
                                                                                                                                                                                          <application
                                                                                                                                                                                              android:theme="@style/Theme.AppCompat.Light.NoActionBar"
                                                                                                                                                                                              />
                                                                                                                                                                                          
                                                                                                                                                                                        4. Add a Toolbar to the activity's layout. For example, the following layout code adds a Toolbar and gives it the appearance of floating above the activity:
                                                                                                                                                                                          <android.support.v7.widget.Toolbar
                                                                                                                                                                                             android:id="@+id/my_toolbar"
                                                                                                                                                                                             android:layout_width="match_parent"
                                                                                                                                                                                             android:layout_height="?attr/actionBarSize"
                                                                                                                                                                                             android:background="?attr/colorPrimary"
                                                                                                                                                                                             android:elevation="4dp"
                                                                                                                                                                                             android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
                                                                                                                                                                                             app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
                                                                                                                                                                                          

                                                                                                                                                                                          The Material Design specification recommends that app bars have an elevation of 4 dp.

                                                                                                                                                                                          Position the toolbar at the top of the activity's layout, since you are using it as an app bar.

                                                                                                                                                                                        5. In the activity's onCreate() method, call the activity's setSupportActionBar() method, and pass the activity's toolbar. This method sets the toolbar as the app bar for the activity. For example:
                                                                                                                                                                                          @Override
                                                                                                                                                                                          protected void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                                              super.onCreate(savedInstanceState);
                                                                                                                                                                                              setContentView(R.layout.activity_my);
                                                                                                                                                                                              Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
                                                                                                                                                                                              setSupportActionBar(myToolbar);
                                                                                                                                                                                              }
                                                                                                                                                                                          

                                                                                                                                                                                        Your app now has a basic action bar. By default, the action bar contains just the name of the app and an overflow menu. The options menu initially contains just the Settings item. You can add more actions to the action bar and the overflow menu, as described in Adding and Handling Actions.

                                                                                                                                                                                        Use App Bar Utility Methods

                                                                                                                                                                                        Once you set the toolbar as an activity's app bar, you have access to the various utility methods provided by the v7 appcompat support library's ActionBar class. This approach lets you do a number of useful things, like hide and show the app bar.

                                                                                                                                                                                        To use the ActionBar utility methods, call the activity's getSupportActionBar() method. This method returns a reference to an appcompat ActionBar object. Once you have that reference, you can call any of the ActionBar methods to adjust the app bar. For example, to hide the app bar, call ActionBar.hide().

                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                        Adding and Handling Actions | Android Developers Skip to content

                                                                                                                                                                                        Most visited

                                                                                                                                                                                        Recently visited

                                                                                                                                                                                        navigation

                                                                                                                                                                                          Adding and Handling Actions

                                                                                                                                                                                          This lesson teaches you to

                                                                                                                                                                                          1. Add Action Buttons
                                                                                                                                                                                          2. Respond to Actions

                                                                                                                                                                                          Useful Resources

                                                                                                                                                                                          Design Guide

                                                                                                                                                                                          Action Buttons

                                                                                                                                                                                          The app bar allows you to add buttons for user actions. This feature lets you put the most important actions for the current context right at the top of the app. For example, a photo browsing app might show share and create album buttons at the top when the user is looking at their photo roll; when the user looks at an individual photo, the app might show crop and filter buttons.

                                                                                                                                                                                          Space in the app bar is limited. If an app declares more actions than can fit in the app bar, the app bar send the excess actions to an overflow menu. The app can also specify that an action should always be shown in the overflow menu, instead of being displayed on the app bar.

                                                                                                                                                                                          Figure 1. An app bar with a single action button and an overflow menu.

                                                                                                                                                                                          Add Action Buttons

                                                                                                                                                                                          All action buttons and other items available in the action overflow are defined in an XML menu resource. To add actions to the action bar, create a new XML file in your project's res/menu/ directory.

                                                                                                                                                                                          Add an <item> element for each item you want to include in the action bar, as shown in this code example of a menu XML file:

                                                                                                                                                                                          <menu xmlns:android="http://schemas.android.com/apk/res/android" >
                                                                                                                                                                                          
                                                                                                                                                                                              <!-- "Mark Favorite", should appear as action button if possible -->
                                                                                                                                                                                              <item
                                                                                                                                                                                                  android:id="@+id/action_favorite"
                                                                                                                                                                                                  android:icon="@drawable/ic_favorite_black_48dp"
                                                                                                                                                                                                  android:title="@string/action_favorite"
                                                                                                                                                                                                  app:showAsAction="ifRoom"/>
                                                                                                                                                                                          
                                                                                                                                                                                              <!-- Settings, should always be in the overflow -->
                                                                                                                                                                                              <item android:id="@+id/action_settings"
                                                                                                                                                                                                    android:title="@string/action_settings"
                                                                                                                                                                                                    app:showAsAction="never"/>
                                                                                                                                                                                          
                                                                                                                                                                                          </menu>
                                                                                                                                                                                          

                                                                                                                                                                                          The app:showAsAction attribute specifies whether the action should be shown as a button on the app bar. If you set app:showAsAction="ifRoom" (as in the example code's favorite action), the action is displayed as a button if there is room in the app bar for it; if there is not enough room, excess actions are sent to the overflow menu. If you set app:showAsAction="never" (as in the example code's settings action), the action is always listed in the overflow menu, not displayed in the app bar.

                                                                                                                                                                                          The system uses the action's icon as the action button if the action is displayed in the app bar. You can find many useful icons on the Material Icons page.

                                                                                                                                                                                          Respond to Actions

                                                                                                                                                                                          When the user selects one of the app bar items, the system calls your activity's onOptionsItemSelected() callback method, and passes a MenuItem object to indicate which item was clicked. In your implementation of onOptionsItemSelected(), call the MenuItem.getItemId() method to determine which item was pressed. The ID returned matches the value you declared in the corresponding <item> element's android:id attribute.

                                                                                                                                                                                          For example, the following code checks to see which action the user selected. If the method does not recognize the user's action, it invokes the superclass method:

                                                                                                                                                                                          @Override
                                                                                                                                                                                          public boolean onOptionsItemSelected(MenuItem item) {
                                                                                                                                                                                              switch (item.getItemId()) {
                                                                                                                                                                                                  case R.id.action_settings:
                                                                                                                                                                                                      // User chose the "Settings" item, show the app settings UI...
                                                                                                                                                                                                      return true;
                                                                                                                                                                                          
                                                                                                                                                                                                  case R.id.action_favorite:
                                                                                                                                                                                                      // User chose the "Favorite" action, mark the current item
                                                                                                                                                                                                      // as a favorite...
                                                                                                                                                                                                      return true;
                                                                                                                                                                                          
                                                                                                                                                                                                  default:
                                                                                                                                                                                                      // If we got here, the user's action was not recognized.
                                                                                                                                                                                                      // Invoke the superclass to handle it.
                                                                                                                                                                                                      return super.onOptionsItemSelected(item);
                                                                                                                                                                                          
                                                                                                                                                                                              }
                                                                                                                                                                                          }
                                                                                                                                                                                          
                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                          Adding an Up Action | Android Developers Skip to content

                                                                                                                                                                                          Most visited

                                                                                                                                                                                          Recently visited

                                                                                                                                                                                          navigation

                                                                                                                                                                                            Adding an Up Action

                                                                                                                                                                                            Your app should make it easy for users to find their way back to the app's main screen. One simple way to do this is to provide an Up button on the app bar for all activities except the main one. When the user selects the Up button, the app navigates to the parent activity.

                                                                                                                                                                                            This lesson shows you how to add an Up button to an activity by declaring the activity's parent in the manifest, and enabling the app bar's Up button.

                                                                                                                                                                                            Declare a Parent Activity

                                                                                                                                                                                            To support the up functionality in an activity, you need to declare the activity's parent. You can do this in the app manifest, by setting an android:parentActivityName attribute.

                                                                                                                                                                                            The android:parentActivityName attribute was introduced in Android 4.1 (API level 16). To support devices with older versions of Android, define a <meta-data> name-value pair, where the name is "android.support.PARENT_ACTIVITY" and the value is the name of the parent activity.

                                                                                                                                                                                            For example, suppose your app has a main activity named MainActivity and a single child activity. The following manifest code declares both activities, and specifies the parent/child relationship:

                                                                                                                                                                                            <application ... >
                                                                                                                                                                                                ...
                                                                                                                                                                                            
                                                                                                                                                                                                <!-- The main/home activity (it has no parent activity) -->
                                                                                                                                                                                            
                                                                                                                                                                                                <activity
                                                                                                                                                                                                    android:name="com.example.myfirstapp.MainActivity" ...>
                                                                                                                                                                                                    ...
                                                                                                                                                                                                </activity>
                                                                                                                                                                                            
                                                                                                                                                                                                <!-- A child of the main activity -->
                                                                                                                                                                                                <activity
                                                                                                                                                                                                    android:name="com.example.myfirstapp.MyChildActivity"
                                                                                                                                                                                                    android:label="@string/title_activity_child"
                                                                                                                                                                                                    android:parentActivityName="com.example.myfirstapp.MainActivity" >
                                                                                                                                                                                            
                                                                                                                                                                                                    <!-- Parent activity meta-data to support 4.0 and lower -->
                                                                                                                                                                                                    <meta-data
                                                                                                                                                                                                        android:name="android.support.PARENT_ACTIVITY"
                                                                                                                                                                                                        android:value="com.example.myfirstapp.MainActivity" />
                                                                                                                                                                                                </activity>
                                                                                                                                                                                            </application>
                                                                                                                                                                                            

                                                                                                                                                                                            Enable the Up Button

                                                                                                                                                                                            To enable the Up button for an activity that has a parent activity, call the app bar's setDisplayHomeAsUpEnabled() method. Typically, you would do this when the activity is created. For example, the following onCreate() method sets a Toolbar as the app bar for MyChildActivity, then enables that app bar's Up button:

                                                                                                                                                                                            @Override
                                                                                                                                                                                            protected void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                                                super.onCreate(savedInstanceState);
                                                                                                                                                                                                setContentView(R.layout.activity_my_child);
                                                                                                                                                                                            
                                                                                                                                                                                                // my_child_toolbar is defined in the layout file
                                                                                                                                                                                                Toolbar myChildToolbar =
                                                                                                                                                                                                    (Toolbar) findViewById(R.id.my_child_toolbar);
                                                                                                                                                                                                setSupportActionBar(myChildToolbar);
                                                                                                                                                                                            
                                                                                                                                                                                                // Get a support ActionBar corresponding to this toolbar
                                                                                                                                                                                                ActionBar ab = getSupportActionBar();
                                                                                                                                                                                            
                                                                                                                                                                                                // Enable the Up button
                                                                                                                                                                                                ab.setDisplayHomeAsUpEnabled(true);
                                                                                                                                                                                            }
                                                                                                                                                                                            

                                                                                                                                                                                            You do not need to catch the up action in the activity's onOptionsItemSelected() method. Instead, that method should call its superclass, as shown in Respond to Actions. The superclass method responds to the Up selection by navigating to the parent activity, as specified in the app manifest.

                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                            Action Views and Action Providers | Android Developers Skip to content

                                                                                                                                                                                            Most visited

                                                                                                                                                                                            Recently visited

                                                                                                                                                                                            navigation

                                                                                                                                                                                              Action Views and Action Providers

                                                                                                                                                                                              This lesson teaches you to

                                                                                                                                                                                              1. Add an Action View
                                                                                                                                                                                              2. Add an Action Provider

                                                                                                                                                                                              The v7 appcompat support library Toolbar provides several different ways for users to interact with your app. Previous lessons described how to define an action, which can be either a button or a menu item. This lesson describes how to add two versatile components:

                                                                                                                                                                                              • An action view is an action that provides rich functionality within the app bar. For example, a search action view allows the user to type their search text in the app bar, without having to change activities or fragments.
                                                                                                                                                                                              • An action provider is an action with its own customized layout. The action initially appears as a button or menu item, but when the user clicks the action, the action provider controls the action's behavior in any way you want to define. For example, the action provider might respond to a click by displaying a menu.

                                                                                                                                                                                              The Android support libraries provide several specialized action view and action provider widgets. For example, the SearchView widget implements an action view for entering search queries, and the ShareActionProvider widget implements an action provider for sharing information with other apps. You can also define your own action views and action providers.

                                                                                                                                                                                              Add an Action View

                                                                                                                                                                                              To add an action view, create an <item> element in the toolbar's menu resource, as Add Action Buttons describes. Add one of the following two attributes to the <item> element:

                                                                                                                                                                                              • actionViewClass: The class of a widget that implements the action.
                                                                                                                                                                                              • actionLayout: A layout resource describing the action's components.

                                                                                                                                                                                              Set the showAsAction attribute to either "ifRoom|collapseActionView" or "never|collapseActionView". The collapseActionView flag indicates how to display the widget when the user is not interacting with it: If the widget is on the app bar, the app should display the widget as an icon. If the widget is in the overflow menu, the app should display the widget as a menu item. When the user interacts with the action view, it expands to fill the app bar.

                                                                                                                                                                                              For example, the following code adds a SearchView widget to the app bar:

                                                                                                                                                                                              <item android:id="@+id/action_search"
                                                                                                                                                                                                   android:title="@string/action_search"
                                                                                                                                                                                                   android:icon="@drawable/ic_search"
                                                                                                                                                                                                   app:showAsAction="ifRoom|collapseActionView"
                                                                                                                                                                                                   app:actionViewClass="android.support.v7.widget.SearchView" />
                                                                                                                                                                                              

                                                                                                                                                                                              If the user is not interacting with the widget, the app displays the widget as the icon specified by android:icon. (If there is not enough room in the app bar, the app adds the action to the overflow menu.) When the user taps the icon or menu item, the widget expands to fill the toolbar, allowing the user to interact with it.

                                                                                                                                                                                              Figure 1. When the user clicks an action view's icon, the view's UI fills the toolbar.

                                                                                                                                                                                              If you need to configure the action, do so in your activity's onCreateOptionsMenu() callback. You can get the action view's object reference by calling the static getActionView() method. For example, the following code gets the object reference for the SearchView widget defined in the previous code example:

                                                                                                                                                                                              @Override
                                                                                                                                                                                              public boolean onCreateOptionsMenu(Menu menu) {
                                                                                                                                                                                                  getMenuInflater().inflate(R.menu.main_activity_actions, menu);
                                                                                                                                                                                              
                                                                                                                                                                                                  MenuItem searchItem = menu.findItem(R.id.action_search);
                                                                                                                                                                                                  SearchView searchView =
                                                                                                                                                                                                          (SearchView) MenuItemCompat.getActionView(searchItem);
                                                                                                                                                                                              
                                                                                                                                                                                                  // Configure the search info and add any event listeners...
                                                                                                                                                                                              
                                                                                                                                                                                                  return super.onCreateOptionsMenu(menu);
                                                                                                                                                                                              }
                                                                                                                                                                                              

                                                                                                                                                                                              Responding to action view expansion

                                                                                                                                                                                              If the action's <item> element has a collapseActionView flag, the app displays the action view as an icon until the user interacts with the action view. When the user clicks on the icon, the built-in handler for onOptionsItemSelected() expands the action view. If your activity subclass overrides the onOptionsItemSelected() method, your override method must call super.onOptionsItemSelected() so the superclass can expand the action view.

                                                                                                                                                                                              If you want to do something when the action is expanded or collapsed, you can define a class that implements MenuItem.OnActionExpandListener, and pass a member of that class to setOnActionExpandListener(). For example, you might want to update the activity based on whether an action view is expanded or collapsed. The following snippet shows how to define and pass a listener:

                                                                                                                                                                                              @Override
                                                                                                                                                                                              public boolean onCreateOptionsMenu(Menu menu) {
                                                                                                                                                                                                  getMenuInflater().inflate(R.menu.options, menu);
                                                                                                                                                                                                  // ...
                                                                                                                                                                                              
                                                                                                                                                                                                  // Define the listener
                                                                                                                                                                                                  OnActionExpandListener expandListener = new OnActionExpandListener() {
                                                                                                                                                                                                      @Override
                                                                                                                                                                                                      public boolean onMenuItemActionCollapse(MenuItem item) {
                                                                                                                                                                                                          // Do something when action item collapses
                                                                                                                                                                                                          return true;  // Return true to collapse action view
                                                                                                                                                                                                      }
                                                                                                                                                                                              
                                                                                                                                                                                                      @Override
                                                                                                                                                                                                      public boolean onMenuItemActionExpand(MenuItem item) {
                                                                                                                                                                                                          // Do something when expanded
                                                                                                                                                                                                          return true;  // Return true to expand action view
                                                                                                                                                                                                      }
                                                                                                                                                                                                  };
                                                                                                                                                                                              
                                                                                                                                                                                                  // Get the MenuItem for the action item
                                                                                                                                                                                                  MenuItem actionMenuItem = menu.findItem(R.id.myActionItem);
                                                                                                                                                                                              
                                                                                                                                                                                                  // Assign the listener to that action item
                                                                                                                                                                                                  MenuItemCompat.setOnActionExpandListener(actionMenuItem, expandListener);
                                                                                                                                                                                              
                                                                                                                                                                                                  // Any other things you have to do when creating the options menu…
                                                                                                                                                                                              
                                                                                                                                                                                                  return true;
                                                                                                                                                                                              }
                                                                                                                                                                                              

                                                                                                                                                                                              Add an Action Provider

                                                                                                                                                                                              To declare an action provider, create an <item> element in the toolbar's menu resource, as described in Add Action Buttons. Add an actionProviderClass attribute, and set it to the fully qualified class name for the action provider class.

                                                                                                                                                                                              For example, the following code declares a ShareActionProvider, which is a widget defined in the support library that allows your app to share data with other apps:

                                                                                                                                                                                              <item android:id="@+id/action_share"
                                                                                                                                                                                                  android:title="@string/share"
                                                                                                                                                                                                  app:showAsAction="ifRoom"
                                                                                                                                                                                                  app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/>
                                                                                                                                                                                              

                                                                                                                                                                                              In this case, it is not necessary to declare an icon for the widget, since ShareActionProvider provides its own graphics. If you are using a custom action, declare an icon.

                                                                                                                                                                                              For information about creating a custom action provider, see the ActionProvider reference. For information about configuring a ShareActionProvider, see the reference for that class.

                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                              Showing Pop-Up Messages | Android Developers Skip to content

                                                                                                                                                                                              Most visited

                                                                                                                                                                                              Recently visited

                                                                                                                                                                                              navigation

                                                                                                                                                                                                Showing Pop-Up Messages

                                                                                                                                                                                                There are many situations where you might want your app to show a quick message to the user, without necessarily waiting for the user to respond. For example, when a user performs an action like sending an email or deleting a file, your app should show a quick confirmation to the user. Often the user doesn't need to respond to the message. The message needs to be prominent enough that the user can see it, but not so prominent that it prevents the user from working with your app.

                                                                                                                                                                                                Android provides the Snackbar widget for this common use case. A Snackbar provides a quick pop-up message to the user. The current activity remains visible and interactive while the Snackbar is displayed. After a short time, the Snackbar automatically dismisses itself.

                                                                                                                                                                                                This class teaches you how to use Snackbar to show pop-up messages.

                                                                                                                                                                                                Figure 1. A Snackbar shows a message at the bottom of the activity, but the rest of the activity is still usable.

                                                                                                                                                                                                Note: The Snackbar class supersedes Toast. While Toast is currently still supported, Snackbar is now the preferred way to display brief, transient messages to the user.

                                                                                                                                                                                                Lessons

                                                                                                                                                                                                Using a Snackbar to Show a Message
                                                                                                                                                                                                Learn how to use a Snackbar to display a brief message to the user.
                                                                                                                                                                                                Adding an Action to a Message
                                                                                                                                                                                                Learn how to add an action to a message, allowing the user to respond to the message.
                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                Building and Displaying a Pop-Up Message | Android Developers Skip to content

                                                                                                                                                                                                Most visited

                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                navigation

                                                                                                                                                                                                  Building and Displaying a Pop-Up Message

                                                                                                                                                                                                  This lesson teaches you to

                                                                                                                                                                                                  1. Use a CoordinatorLayout
                                                                                                                                                                                                  2. Display a Message

                                                                                                                                                                                                  You should also read

                                                                                                                                                                                                  You can use a Snackbar to display a brief message to the user. The message automatically goes away after a short period. A Snackbar is ideal for brief messages that the user doesn't necessarily need to act on. For example, an email app could use a Snackbar to tell the user that the app successfully sent an email.

                                                                                                                                                                                                  Use a CoordinatorLayout

                                                                                                                                                                                                  A Snackbar is attached to a view. The Snackbar provides basic functionality if it is attached to any object derived from the View class, such as any of the common layout objects. However, if the Snackbar is attached to a CoordinatorLayout, the Snackbar gains additional features:

                                                                                                                                                                                                  • The user can dismiss the Snackbar by swiping it away.
                                                                                                                                                                                                  • The layout moves some other UI elements when the Snackbar appears. For example, if the layout has a FloatingActionButton, the layout moves the button up when it shows a Snackbar, instead of drawing the Snackbar on top of the button. You can see how this looks in Figure 1.

                                                                                                                                                                                                  The CoordinatorLayout class provides a superset of the functionality of FrameLayout. If your app already uses a FrameLayout, you can just replace that layout with a CoordinatorLayout to enable the full Snackbar functionality. If your app uses other layout objects, the simplest thing to do is wrap your existing layout elements in a CoordinatorLayout, as in this example:

                                                                                                                                                                                                  <android.support.design.widget.CoordinatorLayout
                                                                                                                                                                                                      android:id="@+id/myCoordinatorLayout"
                                                                                                                                                                                                      xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                      xmlns:app="http://schemas.android.com/apk/res-auto"
                                                                                                                                                                                                      android:layout_width="match_parent"
                                                                                                                                                                                                      android:layout_height="match_parent">
                                                                                                                                                                                                  
                                                                                                                                                                                                      <!-- Here are the existing layout elements, now wrapped in
                                                                                                                                                                                                           a CoordinatorLayout -->
                                                                                                                                                                                                      <LinearLayout
                                                                                                                                                                                                          android:layout_width="match_parent"
                                                                                                                                                                                                          android:layout_height="match_parent"
                                                                                                                                                                                                          android:orientation="vertical">
                                                                                                                                                                                                  
                                                                                                                                                                                                          <!-- …Toolbar, other layouts, other elements… -->
                                                                                                                                                                                                  
                                                                                                                                                                                                      </LinearLayout>
                                                                                                                                                                                                  
                                                                                                                                                                                                  </android.support.design.widget.CoordinatorLayout>

                                                                                                                                                                                                  Make sure to set an android:id tag for your CoordinatorLayout. You need the layout's ID when you display the message.

                                                                                                                                                                                                  Figure 1. The CoordinatorLayout moves the FloatingActionButton up when the Snackbar appears.

                                                                                                                                                                                                  Display a Message

                                                                                                                                                                                                  There are two steps to displaying a message. First, you create a Snackbar object with the message text. Then, you call that object's show() method to display the message to the user.

                                                                                                                                                                                                  Creating a Snackbar object

                                                                                                                                                                                                  Create a Snackbar object by calling the static Snackbar.make() method. When you create the Snackbar, you specify both the message it displays, and the length of time to show the message:

                                                                                                                                                                                                  Snackbar mySnackbar = Snackbar.make(viewId, stringId, duration);
                                                                                                                                                                                                  viewId
                                                                                                                                                                                                  The view to attach the Snackbar to. The method actually searches up the view hierarchy from the passed viewId until it reaches either a CoordinatorLayout, or the window decor's content view. Ordinarily, it's simplest to just pass the ID of the CoordinatorLayout enclosing your content.
                                                                                                                                                                                                  stringId
                                                                                                                                                                                                  The resource ID of the message you want to display. This can be formatted or unformatted text.
                                                                                                                                                                                                  duration
                                                                                                                                                                                                  The length of time to show the message. This can be either LENGTH_SHORT or LENGTH_LONG.

                                                                                                                                                                                                  Showing the message to the user

                                                                                                                                                                                                  Once you have created the Snackbar, call its show() method to display the Snackbar to the user:

                                                                                                                                                                                                  mySnackbar.show();

                                                                                                                                                                                                  The system does not show multiple Snackbar objects at the same time, so if the view is currently displaying another Snackbar, the system queues your Snackbar and displays it after the current Snackbar expires or is dismissed.

                                                                                                                                                                                                  If you just want to show a message to the user and won't need to call any of the Snackbar object's utility methods, you don't need to keep the reference to the Snackbar after you call show(). For this reason, it's common to use method chaining to create and show a Snackbar in one statement:

                                                                                                                                                                                                  Snackbar.make(findViewById(R.id.myCoordinatorLayout), R.string.email_sent,
                                                                                                                                                                                                                          Snackbar.LENGTH_SHORT)
                                                                                                                                                                                                          .show();
                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                  Adding an Action to a Message | Android Developers Skip to content

                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                  navigation

                                                                                                                                                                                                    Adding an Action to a Message

                                                                                                                                                                                                    See Also

                                                                                                                                                                                                    You can add an action to a Snackbar, allowing the user to respond to your message. If you add an action to a Snackbar, the Snackbar puts a button next to the message text. The user can trigger your action by pressing the button. For example, an email app might put an undo button on its "email archived" message; if the user clicks the undo button, the app takes the email back out of the archive.

                                                                                                                                                                                                    Figure 1. This Snackbar has an Undo button, which restores the item that was just removed.

                                                                                                                                                                                                    To add an action to a Snackbar message, you need to define a listener object that implements the View.OnClickListener interface. The system calls your listener's onClick() method if the user clicks on the message action. For example, this snippet shows a listener for an undo action:

                                                                                                                                                                                                    public class MyUndoListener implements View.OnClickListener{
                                                                                                                                                                                                    
                                                                                                                                                                                                        &Override
                                                                                                                                                                                                        public void onClick(View v) {
                                                                                                                                                                                                    
                                                                                                                                                                                                            // Code to undo the user's last action
                                                                                                                                                                                                        }
                                                                                                                                                                                                    }

                                                                                                                                                                                                    Use one of the SetAction() methods to attach the listener to your Snackbar. Be sure to attach the listener before you call show(), as shown in this code sample:

                                                                                                                                                                                                    Snackbar mySnackbar = Snackbar.make(findViewById(R.id.myCoordinatorLayout),
                                                                                                                                                                                                                                    R.string.email_archived, Snackbar.LENGTH_SHORT);
                                                                                                                                                                                                    mySnackbar.setAction(R.string.undo_string, new MyUndoListener());
                                                                                                                                                                                                    mySnackbar.show();

                                                                                                                                                                                                    Note: A Snackbar automatically goes away after a short time, so you can't count on the user seeing the message or having a chance to press the button. For this reason, you should consider offering an alternate way to perform any Snackbar action.

                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                    Creating Custom Views | Android Developers Skip to content

                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                    navigation

                                                                                                                                                                                                      Creating Custom Views

                                                                                                                                                                                                      Dependencies and prerequisites

                                                                                                                                                                                                      • Android 2.1 (API level 7) or higher

                                                                                                                                                                                                      You should also read

                                                                                                                                                                                                      Try it out

                                                                                                                                                                                                      Download the sample

                                                                                                                                                                                                      CustomView.zip

                                                                                                                                                                                                      The Android framework has a large set of View classes for interacting with the user and displaying various types of data. But sometimes your app has unique needs that aren’t covered by the built-in views. This class shows you how to create your own views that are robust and reusable.

                                                                                                                                                                                                      Lessons

                                                                                                                                                                                                      Creating a View Class
                                                                                                                                                                                                      Create a class that acts like a built-in view, with custom attributes and support from the Android Studio layout editor.
                                                                                                                                                                                                      Custom Drawing
                                                                                                                                                                                                      Make your view visually distinctive using the Android graphics system.
                                                                                                                                                                                                      Making the View Interactive
                                                                                                                                                                                                      Users expect a view to react smoothly and naturally to input gestures. This lesson discusses how to use gesture detection, physics, and animation to give your user interface a professional feel.
                                                                                                                                                                                                      Optimizing the View
                                                                                                                                                                                                      No matter how beautiful your UI is, users won't love it if it doesn't run at a consistently high frame rate. Learn how to avoid common performance problems, and how to use hardware acceleration to make your custom drawings run faster.
                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                      Creating a View Class | Android Developers Skip to content

                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                      navigation

                                                                                                                                                                                                        Creating a View Class

                                                                                                                                                                                                        A well-designed custom view is much like any other well-designed class. It encapsulates a specific set of functionality with an easy to use interface, it uses CPU and memory efficiently, and so forth. In addition to being a well-designed class, though, a custom view should:

                                                                                                                                                                                                        • Conform to Android standards
                                                                                                                                                                                                        • Provide custom styleable attributes that work with Android XML layouts
                                                                                                                                                                                                        • Send accessibility events
                                                                                                                                                                                                        • Be compatible with multiple Android platforms.

                                                                                                                                                                                                        The Android framework provides a set of base classes and XML tags to help you create a view that meets all of these requirements. This lesson discusses how to use the Android framework to create the core functionality of a view class.

                                                                                                                                                                                                        Subclass a View

                                                                                                                                                                                                        All of the view classes defined in the Android framework extend View. Your custom view can also extend View directly, or you can save time by extending one of the existing view subclasses, such as Button.

                                                                                                                                                                                                        To allow Android Studio to interact with your view, at a minimum you must provide a constructor that takes a Context and an AttributeSet object as parameters. This constructor allows the layout editor to create and edit an instance of your view.

                                                                                                                                                                                                        class PieChart extends View {
                                                                                                                                                                                                            public PieChart(Context context, AttributeSet attrs) {
                                                                                                                                                                                                                super(context, attrs);
                                                                                                                                                                                                            }
                                                                                                                                                                                                        }
                                                                                                                                                                                                        

                                                                                                                                                                                                        Define Custom Attributes

                                                                                                                                                                                                        To add a built-in View to your user interface, you specify it in an XML element and control its appearance and behavior with element attributes. Well-written custom views can also be added and styled via XML. To enable this behavior in your custom view, you must:

                                                                                                                                                                                                        • Define custom attributes for your view in a <declare-styleable> resource element
                                                                                                                                                                                                        • Specify values for the attributes in your XML layout
                                                                                                                                                                                                        • Retrieve attribute values at runtime
                                                                                                                                                                                                        • Apply the retrieved attribute values to your view

                                                                                                                                                                                                        This section discusses how to define custom attributes and specify their values. The next section deals with retrieving and applying the values at runtime.

                                                                                                                                                                                                        To define custom attributes, add <declare-styleable> resources to your project. It's customary to put these resources into a res/values/attrs.xml file. Here's an example of an attrs.xml file:

                                                                                                                                                                                                        <resources>
                                                                                                                                                                                                           <declare-styleable name="PieChart">
                                                                                                                                                                                                               <attr name="showText" format="boolean" />
                                                                                                                                                                                                               <attr name="labelPosition" format="enum">
                                                                                                                                                                                                                   <enum name="left" value="0"/>
                                                                                                                                                                                                                   <enum name="right" value="1"/>
                                                                                                                                                                                                               </attr>
                                                                                                                                                                                                           </declare-styleable>
                                                                                                                                                                                                        </resources>
                                                                                                                                                                                                        

                                                                                                                                                                                                        This code declares two custom attributes, showText and labelPosition, that belong to a styleable entity named PieChart. The name of the styleable entity is, by convention, the same name as the name of the class that defines the custom view. Although it's not strictly necessary to follow this convention, many popular code editors depend on this naming convention to provide statement completion.

                                                                                                                                                                                                        Once you define the custom attributes, you can use them in layout XML files just like built-in attributes. The only difference is that your custom attributes belong to a different namespace. Instead of belonging to the http://schemas.android.com/apk/res/android namespace, they belong to http://schemas.android.com/apk/res/[your package name]. For example, here's how to use the attributes defined for PieChart:

                                                                                                                                                                                                        <?xml version="1.0" encoding="utf-8"?>
                                                                                                                                                                                                        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                           xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
                                                                                                                                                                                                         <com.example.customviews.charting.PieChart
                                                                                                                                                                                                             custom:showText="true"
                                                                                                                                                                                                             custom:labelPosition="left" />
                                                                                                                                                                                                        </LinearLayout>
                                                                                                                                                                                                        

                                                                                                                                                                                                        In order to avoid having to repeat the long namespace URI, the sample uses an xmlns directive. This directive assigns the alias custom to the namespace http://schemas.android.com/apk/res/com.example.customviews. You can choose any alias you want for your namespace.

                                                                                                                                                                                                        Notice the name of the XML tag that adds the custom view to the layout. It is the fully qualified name of the custom view class. If your view class is an inner class, you must further qualify it with the name of the view's outer class. further. For instance, the PieChart class has an inner class called PieView. To use the custom attributes from this class, you would use the tag com.example.customviews.charting.PieChart$PieView.

                                                                                                                                                                                                        Apply Custom Attributes

                                                                                                                                                                                                        When a view is created from an XML layout, all of the attributes in the XML tag are read from the resource bundle and passed into the view's constructor as an AttributeSet. Although it's possible to read values from the AttributeSet directly, doing so has some disadvantages:

                                                                                                                                                                                                        • Resource references within attribute values are not resolved
                                                                                                                                                                                                        • Styles are not applied

                                                                                                                                                                                                        Instead, pass the AttributeSet to obtainStyledAttributes(). This method passes back a TypedArray array of values that have already been dereferenced and styled.

                                                                                                                                                                                                        The Android resource compiler does a lot of work for you to make calling obtainStyledAttributes() easier. For each <declare-styleable> resource in the res directory, the generated R.java defines both an array of attribute ids and a set of constants that define the index for each attribute in the array. You use the predefined constants to read the attributes from the TypedArray. Here's how the PieChart class reads its attributes:

                                                                                                                                                                                                        public PieChart(Context context, AttributeSet attrs) {
                                                                                                                                                                                                           super(context, attrs);
                                                                                                                                                                                                           TypedArray a = context.getTheme().obtainStyledAttributes(
                                                                                                                                                                                                                attrs,
                                                                                                                                                                                                                R.styleable.PieChart,
                                                                                                                                                                                                                0, 0);
                                                                                                                                                                                                        
                                                                                                                                                                                                           try {
                                                                                                                                                                                                               mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
                                                                                                                                                                                                               mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
                                                                                                                                                                                                           } finally {
                                                                                                                                                                                                               a.recycle();
                                                                                                                                                                                                           }
                                                                                                                                                                                                        }
                                                                                                                                                                                                        

                                                                                                                                                                                                        Note that TypedArray objects are a shared resource and must be recycled after use.

                                                                                                                                                                                                        Add Properties and Events

                                                                                                                                                                                                        Attributes are a powerful way of controlling the behavior and appearance of views, but they can only be read when the view is initialized. To provide dynamic behavior, expose a property getter and setter pair for each custom attribute. The following snippet shows how PieChart exposes a property called showText:

                                                                                                                                                                                                        public boolean isShowText() {
                                                                                                                                                                                                           return mShowText;
                                                                                                                                                                                                        }
                                                                                                                                                                                                        
                                                                                                                                                                                                        public void setShowText(boolean showText) {
                                                                                                                                                                                                           mShowText = showText;
                                                                                                                                                                                                           invalidate();
                                                                                                                                                                                                           requestLayout();
                                                                                                                                                                                                        }
                                                                                                                                                                                                        

                                                                                                                                                                                                        Notice that setShowText calls invalidate() and requestLayout(). These calls are crucial to ensure that the view behaves reliably. You have to invalidate the view after any change to its properties that might change its appearance, so that the system knows that it needs to be redrawn. Likewise, you need to request a new layout if a property changes that might affect the size or shape of the view. Forgetting these method calls can cause hard-to-find bugs.

                                                                                                                                                                                                        Custom views should also support event listeners to communicate important events. For instance, PieChart exposes a custom event called OnCurrentItemChanged to notify listeners that the user has rotated the pie chart to focus on a new pie slice.

                                                                                                                                                                                                        It's easy to forget to expose properties and events, especially when you're the only user of the custom view. Taking some time to carefully define your view's interface reduces future maintenance costs. A good rule to follow is to always expose any property that affects the visible appearance or behavior of your custom view.

                                                                                                                                                                                                        Design For Accessibility

                                                                                                                                                                                                        Your custom view should support the widest range of users. This includes users with disabilities that prevent them from seeing or using a touchscreen. To support users with disabilities, you should:

                                                                                                                                                                                                        • Label your input fields using the android:contentDescription attribute
                                                                                                                                                                                                        • Send accessibility events by calling sendAccessibilityEvent() when appropriate.
                                                                                                                                                                                                        • Support alternate controllers, such as D-pad and trackball

                                                                                                                                                                                                        For more information on creating accessible views, see Making Applications Accessible in the Android Developers Guide.

                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                        Custom Drawing | Android Developers Skip to content

                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                        navigation

                                                                                                                                                                                                          Custom Drawing

                                                                                                                                                                                                          This lesson teaches you to

                                                                                                                                                                                                          1. Override onDraw()
                                                                                                                                                                                                          2. Create Drawing Objects
                                                                                                                                                                                                          3. Handle Layout Events
                                                                                                                                                                                                          4. Draw!

                                                                                                                                                                                                          You should also read

                                                                                                                                                                                                          Try it out

                                                                                                                                                                                                          Download the sample

                                                                                                                                                                                                          CustomView.zip

                                                                                                                                                                                                          The most important part of a custom view is its appearance. Custom drawing can be easy or complex according to your application's needs. This lesson covers some of the most common operations.

                                                                                                                                                                                                          Override onDraw()

                                                                                                                                                                                                          The most important step in drawing a custom view is to override the onDraw() method. The parameter to onDraw() is a Canvas object that the view can use to draw itself. The Canvas class defines methods for drawing text, lines, bitmaps, and many other graphics primitives. You can use these methods in onDraw() to create your custom user interface (UI).

                                                                                                                                                                                                          Before you can call any drawing methods, though, it's necessary to create a Paint object. The next section discusses Paint in more detail.

                                                                                                                                                                                                          Create Drawing Objects

                                                                                                                                                                                                          The android.graphics framework divides drawing into two areas:

                                                                                                                                                                                                          • What to draw, handled by Canvas
                                                                                                                                                                                                          • How to draw, handled by Paint.

                                                                                                                                                                                                          For instance, Canvas provides a method to draw a line, while Paint provides methods to define that line's color. Canvas has a method to draw a rectangle, while Paint defines whether to fill that rectangle with a color or leave it empty. Simply put, Canvas defines shapes that you can draw on the screen, while Paint defines the color, style, font, and so forth of each shape you draw.

                                                                                                                                                                                                          So, before you draw anything, you need to create one or more Paint objects. The PieChart example does this in a method called init, which is called from the constructor:

                                                                                                                                                                                                          private void init() {
                                                                                                                                                                                                             mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
                                                                                                                                                                                                             mTextPaint.setColor(mTextColor);
                                                                                                                                                                                                             if (mTextHeight == 0) {
                                                                                                                                                                                                                 mTextHeight = mTextPaint.getTextSize();
                                                                                                                                                                                                             } else {
                                                                                                                                                                                                                 mTextPaint.setTextSize(mTextHeight);
                                                                                                                                                                                                             }
                                                                                                                                                                                                          
                                                                                                                                                                                                             mPiePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
                                                                                                                                                                                                             mPiePaint.setStyle(Paint.Style.FILL);
                                                                                                                                                                                                             mPiePaint.setTextSize(mTextHeight);
                                                                                                                                                                                                          
                                                                                                                                                                                                             mShadowPaint = new Paint(0);
                                                                                                                                                                                                             mShadowPaint.setColor(0xff101010);
                                                                                                                                                                                                             mShadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));
                                                                                                                                                                                                          
                                                                                                                                                                                                             ...
                                                                                                                                                                                                          

                                                                                                                                                                                                          Creating objects ahead of time is an important optimization. Views are redrawn very frequently, and many drawing objects require expensive initialization. Creating drawing objects within your onDraw() method significantly reduces performance and can make your UI appear sluggish.

                                                                                                                                                                                                          Handle Layout Events

                                                                                                                                                                                                          In order to properly draw your custom view, you need to know what size it is. Complex custom views often need to perform multiple layout calculations depending on the size and shape of their area on screen. You should never make assumptions about the size of your view on the screen. Even if only one app uses your view, that app needs to handle different screen sizes, multiple screen densities, and various aspect ratios in both portrait and landscape mode.

                                                                                                                                                                                                          Although View has many methods for handling measurement, most of them do not need to be overridden. If your view doesn't need special control over its size, you only need to override one method: onSizeChanged().

                                                                                                                                                                                                          onSizeChanged() is called when your view is first assigned a size, and again if the size of your view changes for any reason. Calculate positions, dimensions, and any other values related to your view's size in onSizeChanged(), instead of recalculating them every time you draw. In the PieChart example, onSizeChanged() is where the PieChart view calculates the bounding rectangle of the pie chart and the relative position of the text label and other visual elements.

                                                                                                                                                                                                          When your view is assigned a size, the layout manager assumes that the size includes all of the view's padding. You must handle the padding values when you calculate your view's size. Here's a snippet from PieChart.onSizeChanged() that shows how to do this:

                                                                                                                                                                                                                 // Account for padding
                                                                                                                                                                                                                 float xpad = (float)(getPaddingLeft() + getPaddingRight());
                                                                                                                                                                                                                 float ypad = (float)(getPaddingTop() + getPaddingBottom());
                                                                                                                                                                                                          
                                                                                                                                                                                                                 // Account for the label
                                                                                                                                                                                                                 if (mShowText) xpad += mTextWidth;
                                                                                                                                                                                                          
                                                                                                                                                                                                                 float ww = (float)w - xpad;
                                                                                                                                                                                                                 float hh = (float)h - ypad;
                                                                                                                                                                                                          
                                                                                                                                                                                                                 // Figure out how big we can make the pie.
                                                                                                                                                                                                                 float diameter = Math.min(ww, hh);
                                                                                                                                                                                                          

                                                                                                                                                                                                          If you need finer control over your view's layout parameters, implement onMeasure(). This method's parameters are View.MeasureSpec values that tell you how big your view's parent wants your view to be, and whether that size is a hard maximum or just a suggestion. As an optimization, these values are stored as packed integers, and you use the static methods of View.MeasureSpec to unpack the information stored in each integer.

                                                                                                                                                                                                          Here's an example implementation of onMeasure(). In this implementation, PieChart attempts to make its area big enough to make the pie as big as its label:

                                                                                                                                                                                                          @Override
                                                                                                                                                                                                          protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                                                                                                                                                                                                             // Try for a width based on our minimum
                                                                                                                                                                                                             int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
                                                                                                                                                                                                             int w = resolveSizeAndState(minw, widthMeasureSpec, 1);
                                                                                                                                                                                                          
                                                                                                                                                                                                             // Whatever the width ends up being, ask for a height that would let the pie
                                                                                                                                                                                                             // get as big as it can
                                                                                                                                                                                                             int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
                                                                                                                                                                                                             int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);
                                                                                                                                                                                                          
                                                                                                                                                                                                             setMeasuredDimension(w, h);
                                                                                                                                                                                                          }
                                                                                                                                                                                                          

                                                                                                                                                                                                          There are three important things to note in this code:

                                                                                                                                                                                                          • The calculations take into account the view's padding. As mentioned earlier, this is the view's responsibility.
                                                                                                                                                                                                          • The helper method resolveSizeAndState() is used to create the final width and height values. This helper returns an appropriate View.MeasureSpec value by comparing the view's desired size to the spec passed into onMeasure().
                                                                                                                                                                                                          • onMeasure() has no return value. Instead, the method communicates its results by calling setMeasuredDimension(). Calling this method is mandatory. If you omit this call, the View class throws a runtime exception.

                                                                                                                                                                                                          Draw!

                                                                                                                                                                                                          Once you have your object creation and measuring code defined, you can implement onDraw(). Every view implements onDraw() differently, but there are some common operations that most views share:

                                                                                                                                                                                                          For example, here's the code that draws PieChart. It uses a mix of text, lines, and shapes.

                                                                                                                                                                                                          protected void onDraw(Canvas canvas) {
                                                                                                                                                                                                             super.onDraw(canvas);
                                                                                                                                                                                                          
                                                                                                                                                                                                             // Draw the shadow
                                                                                                                                                                                                             canvas.drawOval(
                                                                                                                                                                                                                     mShadowBounds,
                                                                                                                                                                                                                     mShadowPaint
                                                                                                                                                                                                             );
                                                                                                                                                                                                          
                                                                                                                                                                                                             // Draw the label text
                                                                                                                                                                                                             canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);
                                                                                                                                                                                                          
                                                                                                                                                                                                             // Draw the pie slices
                                                                                                                                                                                                             for (int i = 0; i < mData.size(); ++i) {
                                                                                                                                                                                                                 Item it = mData.get(i);
                                                                                                                                                                                                                 mPiePaint.setShader(it.mShader);
                                                                                                                                                                                                                 canvas.drawArc(mBounds,
                                                                                                                                                                                                                         360 - it.mEndAngle,
                                                                                                                                                                                                                         it.mEndAngle - it.mStartAngle,
                                                                                                                                                                                                                         true, mPiePaint);
                                                                                                                                                                                                             }
                                                                                                                                                                                                          
                                                                                                                                                                                                             // Draw the pointer
                                                                                                                                                                                                             canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
                                                                                                                                                                                                             canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
                                                                                                                                                                                                          }
                                                                                                                                                                                                          
                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                          Making the View Interactive | Android Developers Skip to content

                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                          navigation

                                                                                                                                                                                                            Making the View Interactive

                                                                                                                                                                                                            Drawing a UI is only one part of creating a custom view. You also need to make your view respond to user input in a way that closely resembles the real-world action you're mimicking. Objects should always act in the same way that real objects do. For example, images should not immediately pop out of existence and reappear somewhere else, because objects in the real world don't do that. Instead, images should move from one place to another.

                                                                                                                                                                                                            Users also sense subtle behavior or feel in an interface, and react best to subtleties that mimic the real world. For example, when users fling a UI object, they should sense friction at the beginning that delays the motion, and then at the end sense momentum that carries the motion beyond the fling.

                                                                                                                                                                                                            This lesson demonstrates how to use features of the Android framework to add these real-world behaviors to your custom view.

                                                                                                                                                                                                            Handle Input Gestures

                                                                                                                                                                                                            Like many other UI frameworks, Android supports an input event model. User actions are turned into events that trigger callbacks, and you can override the callbacks to customize how your application responds to the user. The most common input event in the Android system is touch, which triggers onTouchEvent(android.view.MotionEvent). Override this method to handle the event:

                                                                                                                                                                                                               @Override
                                                                                                                                                                                                               public boolean onTouchEvent(MotionEvent event) {
                                                                                                                                                                                                                return super.onTouchEvent(event);
                                                                                                                                                                                                               }
                                                                                                                                                                                                            

                                                                                                                                                                                                            Touch events by themselves are not particularly useful. Modern touch UIs define interactions in terms of gestures such as tapping, pulling, pushing, flinging, and zooming. To convert raw touch events into gestures, Android provides GestureDetector.

                                                                                                                                                                                                            Construct a GestureDetector by passing in an instance of a class that implements GestureDetector.OnGestureListener. If you only want to process a few gestures, you can extend GestureDetector.SimpleOnGestureListener instead of implementing the GestureDetector.OnGestureListener interface. For instance, this code creates a class that extends GestureDetector.SimpleOnGestureListener and overrides onDown(MotionEvent).

                                                                                                                                                                                                            class mListener extends GestureDetector.SimpleOnGestureListener {
                                                                                                                                                                                                               @Override
                                                                                                                                                                                                               public boolean onDown(MotionEvent e) {
                                                                                                                                                                                                                   return true;
                                                                                                                                                                                                               }
                                                                                                                                                                                                            }
                                                                                                                                                                                                            mDetector = new GestureDetector(PieChart.this.getContext(), new mListener());
                                                                                                                                                                                                            

                                                                                                                                                                                                            Whether or not you use GestureDetector.SimpleOnGestureListener, you must always implement an onDown() method that returns true. This step is necessary because all gestures begin with an onDown() message. If you return false from onDown(), as GestureDetector.SimpleOnGestureListener does, the system assumes that you want to ignore the rest of the gesture, and the other methods of GestureDetector.OnGestureListener never get called. The only time you should return false from onDown() is if you truly want to ignore an entire gesture. Once you've implemented GestureDetector.OnGestureListener and created an instance of GestureDetector, you can use your GestureDetector to interpret the touch events you receive in onTouchEvent().

                                                                                                                                                                                                            @Override
                                                                                                                                                                                                            public boolean onTouchEvent(MotionEvent event) {
                                                                                                                                                                                                               boolean result = mDetector.onTouchEvent(event);
                                                                                                                                                                                                               if (!result) {
                                                                                                                                                                                                                   if (event.getAction() == MotionEvent.ACTION_UP) {
                                                                                                                                                                                                                       stopScrolling();
                                                                                                                                                                                                                       result = true;
                                                                                                                                                                                                                   }
                                                                                                                                                                                                               }
                                                                                                                                                                                                               return result;
                                                                                                                                                                                                            }
                                                                                                                                                                                                            

                                                                                                                                                                                                            When you pass onTouchEvent() a touch event that it doesn't recognize as part of a gesture, it returns false. You can then run your own custom gesture-detection code.

                                                                                                                                                                                                            Create Physically Plausible Motion

                                                                                                                                                                                                            Gestures are a powerful way to control touchscreen devices, but they can be counterintuitive and difficult to remember unless they produce physically plausible results. A good example of this is the fling gesture, where the user quickly moves a finger across the screen and then lifts it. This gesture makes sense if the UI responds by moving quickly in the direction of the fling, then slowing down, as if the user had pushed on a flywheel and set it spinning.

                                                                                                                                                                                                            However, simulating the feel of a flywheel isn't trivial. A lot of physics and math are required to get a flywheel model working correctly. Fortunately, Android provides helper classes to simulate this and other behaviors. The Scroller class is the basis for handling flywheel-style fling gestures.

                                                                                                                                                                                                            To start a fling, call fling() with the starting velocity and the minimum and maximum x and y values of the fling. For the velocity value, you can use the value computed for you by GestureDetector.

                                                                                                                                                                                                            @Override
                                                                                                                                                                                                            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                                                                                                                                                                                                               mScroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);
                                                                                                                                                                                                               postInvalidate();
                                                                                                                                                                                                            }
                                                                                                                                                                                                            

                                                                                                                                                                                                            Note: Although the velocity calculated by GestureDetector is physically accurate, many developers feel that using this value makes the fling animation too fast. It's common to divide the x and y velocity by a factor of 4 to 8.

                                                                                                                                                                                                            The call to fling() sets up the physics model for the fling gesture. Afterwards, you need to update the Scroller by calling Scroller.computeScrollOffset() at regular intervals. computeScrollOffset() updates the Scroller object's internal state by reading the current time and using the physics model to calculate the x and y position at that time. Call getCurrX() and getCurrY() to retrieve these values.

                                                                                                                                                                                                            Most views pass the Scroller object's x and y position directly to scrollTo(). The PieChart example is a little different: it uses the current scroll y position to set the rotational angle of the chart.

                                                                                                                                                                                                            if (!mScroller.isFinished()) {
                                                                                                                                                                                                                mScroller.computeScrollOffset();
                                                                                                                                                                                                                setPieRotation(mScroller.getCurrY());
                                                                                                                                                                                                            }
                                                                                                                                                                                                            

                                                                                                                                                                                                            The Scroller class computes scroll positions for you, but it does not automatically apply those positions to your view. It's your responsibility to make sure you get and apply new coordinates often enough to make the scrolling animation look smooth. There are two ways to do this:

                                                                                                                                                                                                            The PieChart example uses the second approach. This technique is slightly more complex to set up, but it works more closely with the animation system and doesn't require potentially unnecessary view invalidation. The drawback is that ValueAnimator is not available prior to API level 11, so this technique cannot be used on devices running Android versions lower than 3.0.

                                                                                                                                                                                                            Note: You can use ValueAnimator in applications that target lower API levels. You just need to make sure to check the current API level at runtime, and omit the calls to the view animation system if the current level is less than 11.

                                                                                                                                                                                                                   mScroller = new Scroller(getContext(), null, true);
                                                                                                                                                                                                                   mScrollAnimator = ValueAnimator.ofFloat(0,1);
                                                                                                                                                                                                                   mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                                                                                                                                                                                                                       @Override
                                                                                                                                                                                                                       public void onAnimationUpdate(ValueAnimator valueAnimator) {
                                                                                                                                                                                                                           if (!mScroller.isFinished()) {
                                                                                                                                                                                                                               mScroller.computeScrollOffset();
                                                                                                                                                                                                                               setPieRotation(mScroller.getCurrY());
                                                                                                                                                                                                                           } else {
                                                                                                                                                                                                                               mScrollAnimator.cancel();
                                                                                                                                                                                                                               onScrollFinished();
                                                                                                                                                                                                                           }
                                                                                                                                                                                                                       }
                                                                                                                                                                                                                   });
                                                                                                                                                                                                            

                                                                                                                                                                                                            Make Your Transitions Smooth

                                                                                                                                                                                                            Users expect a modern UI to transition smoothly between states. UI elements fade in and out instead of appearing and disappearing. Motions begin and end smoothly instead of starting and stopping abruptly. The Android property animation framework, introduced in Android 3.0, makes smooth transitions easy.

                                                                                                                                                                                                            To use the animation system, whenever a property changes that will affect your view's appearance, do not change the property directly. Instead, use ValueAnimator to make the change. In the following example, modifying the currently selected pie slice in PieChart causes the entire chart to rotate so that the selection pointer is centered in the selected slice. ValueAnimator changes the rotation over a period of several hundred milliseconds, rather than immediately setting the new rotation value.

                                                                                                                                                                                                            mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this, "PieRotation", 0);
                                                                                                                                                                                                            mAutoCenterAnimator.setIntValues(targetAngle);
                                                                                                                                                                                                            mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
                                                                                                                                                                                                            mAutoCenterAnimator.start();
                                                                                                                                                                                                            

                                                                                                                                                                                                            If the value you want to change is one of the base View properties, doing the animation is even easier, because Views have a built-in ViewPropertyAnimator that is optimized for simultaneous animation of multiple properties. For example:

                                                                                                                                                                                                            animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();
                                                                                                                                                                                                            
                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                            Optimizing the View | Android Developers Skip to content

                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                            navigation

                                                                                                                                                                                                              Optimizing the View

                                                                                                                                                                                                              This lesson teaches you to

                                                                                                                                                                                                              Try it out

                                                                                                                                                                                                              Download the sample

                                                                                                                                                                                                              CustomView.zip

                                                                                                                                                                                                              Now that you have a well-designed view that responds to gestures and transitions between states, ensure that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, ensure that animations consistently run at 60 frames per second.

                                                                                                                                                                                                              Do Less, Less Frequently

                                                                                                                                                                                                              To speed up your view, eliminate unnecessary code from routines that are called frequently. Start by working on onDraw(), which will give you the biggest payback. In particular you should eliminate allocations in onDraw(), because allocations may lead to a garbage collection that would cause a stutter. Allocate objects during initialization, or between animations. Never make an allocation while an animation is running.

                                                                                                                                                                                                              In addition to making onDraw() leaner, also make sure it's called as infrequently as possible. Most calls to onDraw() are the result of a call to invalidate(), so eliminate unnecessary calls to invalidate().

                                                                                                                                                                                                              Another very expensive operation is traversing layouts. Any time a view calls requestLayout(), the Android UI system needs to traverse the entire view hierarchy to find out how big each view needs to be. If it finds conflicting measurements, it may need to traverse the hierarchy multiple times. UI designers sometimes create deep hierarchies of nested ViewGroup objects in order to get the UI to behave properly. These deep view hierarchies cause performance problems. Make your view hierarchies as shallow as possible.

                                                                                                                                                                                                              If you have a complex UI, consider writing a custom ViewGroup to perform its layout. Unlike the built-in views, your custom view can make application-specific assumptions about the size and shape of its children, and thus avoid traversing its children to calculate measurements. The PieChart example shows how to extend ViewGroup as part of a custom view. PieChart has child views, but it never measures them. Instead, it sets their sizes directly according to its own custom layout algorithm.

                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                              Creating Backward-Compatible UIs | Android Developers Skip to content

                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                Creating Backward-Compatible UIs

                                                                                                                                                                                                                Dependencies and prerequisites

                                                                                                                                                                                                                You should also read

                                                                                                                                                                                                                Try it out

                                                                                                                                                                                                                Download the sample app

                                                                                                                                                                                                                TabCompat.zip

                                                                                                                                                                                                                This class demonstrates how to use UI components and APIs available in newer versions of Android in a backward-compatible way, ensuring that your application still runs on previous versions of the platform.

                                                                                                                                                                                                                Throughout this class, the new Action Bar Tabs feature introduced in Android 3.0 (API level 11) serves as the guiding example, but you can apply these techniques to other UI components and API features.

                                                                                                                                                                                                                Lessons

                                                                                                                                                                                                                Abstracting the New APIs
                                                                                                                                                                                                                Determine which features and APIs your application needs. Learn how to define application-specific, intermediary Java interfaces that abstract the implementation of the UI component to your application.
                                                                                                                                                                                                                Proxying to the New APIs
                                                                                                                                                                                                                Learn how to create an implementation of your interface that uses newer APIs.
                                                                                                                                                                                                                Creating an Implementation with Older APIs
                                                                                                                                                                                                                Learn how to create a custom implementation of your interface that uses older APIs.
                                                                                                                                                                                                                Using the Version-Aware Component
                                                                                                                                                                                                                Learn how to choose an implementation to use at runtime, and begin using the interface in your application.
                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                Implementing Accessibility | Android Developers Skip to content

                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                  Implementing Accessibility

                                                                                                                                                                                                                  Dependencies and prerequisites

                                                                                                                                                                                                                  • Android 2.0 (API Level 5) or higher

                                                                                                                                                                                                                  You should also read

                                                                                                                                                                                                                  When it comes to reaching as wide a userbase as possible, it's important to pay attention to accessibility in your Android application. Cues in your user interface that may work for a majority of users, such as a visible change in state when a button is pressed, can be less optimal if the user is visually impaired.

                                                                                                                                                                                                                  This class shows you how to make the most of the accessibility features built into the Android framework. It covers how to optimize your app for accessibility, leveraging platform features like focus navigation and content descriptions. It also covers how to build accessibility services, that can facilitate user interaction with any Android application, not just your own.

                                                                                                                                                                                                                  Lessons

                                                                                                                                                                                                                  Developing Accessible Applications
                                                                                                                                                                                                                  Learn to make your Android application accessible. Allow for easy navigation with a keyboard or directional pad, set labels and fire events that can be interpreted by an accessibility service to facilitate a smooth user experience.
                                                                                                                                                                                                                  Developing Accessibility Services
                                                                                                                                                                                                                  Develop an accessibility service that listens for accessibility events, mines those events for information like event type and content descriptions, and uses that information to communicate with the user. The example will use a text-to-speech engine to speak to the user.
                                                                                                                                                                                                                  Accessibility Checklist
                                                                                                                                                                                                                  Learn how to test your app for accessibility.
                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                  Developing Accessible Applications | Android Developers Skip to content

                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                    Developing Accessible Applications

                                                                                                                                                                                                                    Android has several accessibility-focused features baked into the platform, which make it easy to optimize your application for those with visual or physical disabilities. However, it's not always obvious what the correct optimizations are, or the easiest way to leverage the framework toward this purpose. This lesson shows you how to implement the strategies and platform features that make for a great accessibility-enabled Android application.

                                                                                                                                                                                                                    Add Content Descriptions

                                                                                                                                                                                                                    A well-designed user interface (UI) often has elements that don't require an explicit label to indicate their purpose to the user. A checkbox next to an item in a task list application has a fairly obvious purpose, as does a trash can in a file manager application. However, to your users with vision impairment, other UI cues are needed.

                                                                                                                                                                                                                    Fortunately, it's easy to add labels to UI elements in your application that can be read out loud to your user by a speech-based accessibility service like TalkBack . If you have a label that's likely not to change during the lifecycle of the application (such as "Pause" or "Purchase"), you can add it via the XML layout, by setting a UI element's android:contentDescription attribute, like in this example:

                                                                                                                                                                                                                    <Button
                                                                                                                                                                                                                        android:id=”@+id/pause_button”
                                                                                                                                                                                                                        android:src=”@drawable/pause”
                                                                                                                                                                                                                        android:contentDescription=”@string/pause”/>
                                                                                                                                                                                                                    

                                                                                                                                                                                                                    However, there are plenty of situations where it's desirable to base the content description on some context, such as the state of a toggle button, or a piece selectable data like a list item. To edit the content description at runtime, use the setContentDescription() method, like this:

                                                                                                                                                                                                                    String contentDescription = "Select " + strValues[position];
                                                                                                                                                                                                                    label.setContentDescription(contentDescription);
                                                                                                                                                                                                                    

                                                                                                                                                                                                                    This addition to your code is the simplest accessibility improvement you can make to your application, but one of the most useful. Try to add content descriptions wherever there's useful information, but avoid the web-developer pitfall of labelling everything with useless information. For instance, don't set an application icon's content description to "app icon". That just increases the noise a user needs to navigate in order to pull useful information from your interface.

                                                                                                                                                                                                                    Try it out! Download TalkBack (an accessibility service published by Google) and enable it in Settings > Accessibility > TalkBack. Then navigate around your own application and listen for the audible cues provided by TalkBack.

                                                                                                                                                                                                                    Design for Focus Navigation

                                                                                                                                                                                                                    Your application should support more methods of navigation than the touch screen alone. Many Android devices come with navigation hardware other than the touchscreen, like a D-Pad, arrow keys, or a trackball. In addition, later Android releases also support connecting external devices like keyboards via USB or bluetooth.

                                                                                                                                                                                                                    In order to enable this form of navigation, all navigational elements that the user should be able to navigate to need to be set as focusable. This modification can be done at runtime using the View.setFocusable() method on that UI control, or by setting the android:focusable attrubute in your XML layout files.

                                                                                                                                                                                                                    Also, each UI control has 4 attributes, android:nextFocusUp, android:nextFocusDown, android:nextFocusLeft, and android:nextFocusRight, which you can use to designate the next view to receive focus when the user navigates in that direction. While the platform determines navigation sequences automatically based on layout proximity, you can use these attributes to override that sequence if it isn't appropriate in your application.

                                                                                                                                                                                                                    For instance, here's how you represent a button and label, both focusable, such that pressing down takes you from the button to the text view, and pressing up would take you back to the button.

                                                                                                                                                                                                                    <Button android:id="@+id/doSomething"
                                                                                                                                                                                                                        android:focusable="true"
                                                                                                                                                                                                                        android:nextFocusDown=”@id/label”
                                                                                                                                                                                                                        ... />
                                                                                                                                                                                                                    <TextView android:id="@+id/label"
                                                                                                                                                                                                                        android:focusable=”true”
                                                                                                                                                                                                                        android:text="@string/labelText"
                                                                                                                                                                                                                        android:nextFocusUp=”@id/doSomething”
                                                                                                                                                                                                                        ... />
                                                                                                                                                                                                                    

                                                                                                                                                                                                                    Verify that your application works intuitively in these situations. The easiest way is to simply run your application in the Android emulator, and navigate around the UI with the emulator's arrow keys, using the OK button as a replacement for touch to select UI controls.

                                                                                                                                                                                                                    Fire Accessibility Events

                                                                                                                                                                                                                    If you're using the view components in the Android framework, an AccessibilityEvent is created whenever you select an item or change focus in your UI. These events are examined by the accessibility service, enabling it to provide features like text-to-speech to the user.

                                                                                                                                                                                                                    If you write a custom view, make sure it fires events at the appropriate times. Generate events by calling sendAccessibilityEvent(int), with a parameter representing the type of event that occurred. A complete list of the event types currently supported can be found in the AccessibilityEvent reference documentation.

                                                                                                                                                                                                                    As an example, if you want to extend an image view such that you can write captions by typing on the keyboard when it has focus, it makes sense to fire an TYPE_VIEW_TEXT_CHANGED event, even though that's not normally built into image views. The code to generate that event would look like this:

                                                                                                                                                                                                                    public void onTextChanged(String before, String after) {
                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                                                                                                                                                                                                                            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
                                                                                                                                                                                                                        }
                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                    }
                                                                                                                                                                                                                    

                                                                                                                                                                                                                    Test Your Application

                                                                                                                                                                                                                    Be sure to test the accessibility functionality as you add it to your application. In order to test the content descriptions and Accessibility events, install and enable an accessibility service. One option is Talkback , a free, open source screen reader available on Google Play. With the service enabled, test all the navigation flows through your application and listen to the spoken feedback.

                                                                                                                                                                                                                    Also, attempt to navigate your application using a directional controller, instead of the touch screen. You can use a physical device with a d-pad or trackball if one is available. If not, use the Android emulator and it's simulated keyboard controls.

                                                                                                                                                                                                                    Between the service providing feedback and the directional navigation through your application, you should get a sense of what your application is like to navigate without any visual cues. Fix problem areas as they appear, and you'll end up with with a more accessible Android application.

                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                    Developing an Accessibility Service | Android Developers Skip to content

                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                      Developing an Accessibility Service

                                                                                                                                                                                                                      Accessibility services are a feature of the Android framework designed to provide alternative navigation feedback to the user on behalf of applications installed on Android devices. An accessibility service can communicate to the user on the application's behalf, such as converting text to speech, or haptic feedback when a user is hovering on an important area of the screen. This lesson covers how to create an accessibility service, process information received from the application, and report that information back to the user.

                                                                                                                                                                                                                      Create Your Accessibility Service

                                                                                                                                                                                                                      An accessibility service can be bundled with a normal application, or created as a standalone Android project. The steps to creating the service are the same in either situation. Within your project, create a class that extends AccessibilityService.

                                                                                                                                                                                                                      package com.example.android.apis.accessibility;
                                                                                                                                                                                                                      
                                                                                                                                                                                                                      import android.accessibilityservice.AccessibilityService;
                                                                                                                                                                                                                      
                                                                                                                                                                                                                      public class MyAccessibilityService extends AccessibilityService {
                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                          @Override
                                                                                                                                                                                                                          public void onAccessibilityEvent(AccessibilityEvent event) {
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          @Override
                                                                                                                                                                                                                          public void onInterrupt() {
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                      }
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      Like any other service, you also declare it in the manifest file. Remember to specify that it handles the android.accessibilityservice intent, so that the service is called when applications fire an AccessibilityEvent.

                                                                                                                                                                                                                      <application ...>
                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                      <service android:name=".MyAccessibilityService">
                                                                                                                                                                                                                           <intent-filter>
                                                                                                                                                                                                                               <action android:name="android.accessibilityservice.AccessibilityService" />
                                                                                                                                                                                                                           </intent-filter>
                                                                                                                                                                                                                           . . .
                                                                                                                                                                                                                      </service>
                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                      </application>
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      If you created a new project for this service, and don't plan on having an application, you can remove the starter Activity class (usually called MainActivity.java) from your source. Remember to also remove the corresponding activity element from your manifest.

                                                                                                                                                                                                                      Configure Your Accessibility Service

                                                                                                                                                                                                                      Setting the configuration variables for your accessibility service tells the system how and when you want it to run. Which event types would you like to respond to? Should the service be active for all applications, or only specific package names? What different feedback types does it use?

                                                                                                                                                                                                                      You have two options for how to set these variables. The backwards-compatible option is to set them in code, using setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo). To do that, override the onServiceConnected() method and configure your service in there.

                                                                                                                                                                                                                      @Override
                                                                                                                                                                                                                      public void onServiceConnected() {
                                                                                                                                                                                                                          // Set the type of events that this service wants to listen to.  Others
                                                                                                                                                                                                                          // won't be passed to this service.
                                                                                                                                                                                                                          info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
                                                                                                                                                                                                                                  AccessibilityEvent.TYPE_VIEW_FOCUSED;
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // If you only want this service to work with specific applications, set their
                                                                                                                                                                                                                          // package names here.  Otherwise, when the service is activated, it will listen
                                                                                                                                                                                                                          // to events from all applications.
                                                                                                                                                                                                                          info.packageNames = new String[]
                                                                                                                                                                                                                                  {"com.example.android.myFirstApp", "com.example.android.mySecondApp"};
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // Set the type of feedback your service will provide.
                                                                                                                                                                                                                          info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // Default services are invoked only if no package-specific ones are present
                                                                                                                                                                                                                          // for the type of AccessibilityEvent generated.  This service *is*
                                                                                                                                                                                                                          // application-specific, so the flag isn't necessary.  If this was a
                                                                                                                                                                                                                          // general-purpose service, it would be worth considering setting the
                                                                                                                                                                                                                          // DEFAULT flag.
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // info.flags = AccessibilityServiceInfo.DEFAULT;
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          info.notificationTimeout = 100;
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          this.setServiceInfo(info);
                                                                                                                                                                                                                      
                                                                                                                                                                                                                      }
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      The second option is to configure the service using an XML file. Certain configuration options like canRetrieveWindowContent are only available if you configure your service using XML. The same configuration options above, defined using XML, would look like this:

                                                                                                                                                                                                                      <accessibility-service
                                                                                                                                                                                                                           android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
                                                                                                                                                                                                                           android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
                                                                                                                                                                                                                           android:accessibilityFeedbackType="feedbackSpoken"
                                                                                                                                                                                                                           android:notificationTimeout="100"
                                                                                                                                                                                                                           android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
                                                                                                                                                                                                                           android:canRetrieveWindowContent="true"
                                                                                                                                                                                                                      />
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      If you go the XML route, be sure to reference it in your manifest, by adding a <meta-data> tag to your service declaration, pointing at the XML file. If you stored your XML file in res/xml/serviceconfig.xml, the new tag would look like this:

                                                                                                                                                                                                                      <service android:name=".MyAccessibilityService">
                                                                                                                                                                                                                           <intent-filter>
                                                                                                                                                                                                                               <action android:name="android.accessibilityservice.AccessibilityService" />
                                                                                                                                                                                                                           </intent-filter>
                                                                                                                                                                                                                           <meta-data android:name="android.accessibilityservice"
                                                                                                                                                                                                                           android:resource="@xml/serviceconfig" />
                                                                                                                                                                                                                      </service>
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      Respond to AccessibilityEvents

                                                                                                                                                                                                                      Now that your service is set up to run and listen for events, write some code so it knows what to do when an AccessibilityEvent actually arrives! Start by overriding the onAccessibilityEvent(AccessibilityEvent) method. In that method, use getEventType() to determine the type of event, and getContentDescription() to extract any label text associated with the view that fired the event.

                                                                                                                                                                                                                      @Override
                                                                                                                                                                                                                      public void onAccessibilityEvent(AccessibilityEvent event) {
                                                                                                                                                                                                                          final int eventType = event.getEventType();
                                                                                                                                                                                                                          String eventText = null;
                                                                                                                                                                                                                          switch(eventType) {
                                                                                                                                                                                                                              case AccessibilityEvent.TYPE_VIEW_CLICKED:
                                                                                                                                                                                                                                  eventText = "Focused: ";
                                                                                                                                                                                                                                  break;
                                                                                                                                                                                                                              case AccessibilityEvent.TYPE_VIEW_FOCUSED:
                                                                                                                                                                                                                                  eventText = "Focused: ";
                                                                                                                                                                                                                                  break;
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          eventText = eventText + event.getContentDescription();
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // Do something nifty with this text, like speak the composed string
                                                                                                                                                                                                                          // back to the user.
                                                                                                                                                                                                                          speakToUser(eventText);
                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                      }
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      Query the View Heirarchy for More Context

                                                                                                                                                                                                                      This step is optional, but highly useful. The Android platform provides the ability for an AccessibilityService to query the view hierarchy, collecting information about the UI component that generated an event, and its parent and children. In order to do this, make sure that you set the following line in your XML configuration:

                                                                                                                                                                                                                      android:canRetrieveWindowContent="true"
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      Once that's done, get an AccessibilityNodeInfo object using getSource(). This call only returns an object if the window where the event originated is still the active window. If not, it will return null, so behave accordingly. The following example is a snippet of code that, when it receives an event, does the following:

                                                                                                                                                                                                                      1. Immediately grab the parent of the view where the event originated
                                                                                                                                                                                                                      2. In that view, look for a label and a check box as children views
                                                                                                                                                                                                                      3. If it finds them, create a string to report to the user, indicating the label and whether it was checked or not.
                                                                                                                                                                                                                      4. If at any point a null value is returned while traversing the view hierarchy, the method quietly gives up.
                                                                                                                                                                                                                      
                                                                                                                                                                                                                      // Alternative onAccessibilityEvent, that uses AccessibilityNodeInfo
                                                                                                                                                                                                                      
                                                                                                                                                                                                                      @Override
                                                                                                                                                                                                                      public void onAccessibilityEvent(AccessibilityEvent event) {
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          AccessibilityNodeInfo source = event.getSource();
                                                                                                                                                                                                                          if (source == null) {
                                                                                                                                                                                                                              return;
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // Grab the parent of the view that fired the event.
                                                                                                                                                                                                                          AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
                                                                                                                                                                                                                          if (rowNode == null) {
                                                                                                                                                                                                                              return;
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // Using this parent, get references to both child nodes, the label and the checkbox.
                                                                                                                                                                                                                          AccessibilityNodeInfo labelNode = rowNode.getChild(0);
                                                                                                                                                                                                                          if (labelNode == null) {
                                                                                                                                                                                                                              rowNode.recycle();
                                                                                                                                                                                                                              return;
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          AccessibilityNodeInfo completeNode = rowNode.getChild(1);
                                                                                                                                                                                                                          if (completeNode == null) {
                                                                                                                                                                                                                              rowNode.recycle();
                                                                                                                                                                                                                              return;
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          // Determine what the task is and whether or not it's complete, based on
                                                                                                                                                                                                                          // the text inside the label, and the state of the check-box.
                                                                                                                                                                                                                          if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
                                                                                                                                                                                                                              rowNode.recycle();
                                                                                                                                                                                                                              return;
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          CharSequence taskLabel = labelNode.getText();
                                                                                                                                                                                                                          final boolean isComplete = completeNode.isChecked();
                                                                                                                                                                                                                          String completeStr = null;
                                                                                                                                                                                                                      
                                                                                                                                                                                                                          if (isComplete) {
                                                                                                                                                                                                                              completeStr = getString(R.string.checked);
                                                                                                                                                                                                                          } else {
                                                                                                                                                                                                                              completeStr = getString(R.string.not_checked);
                                                                                                                                                                                                                          }
                                                                                                                                                                                                                          String reportStr = taskLabel + completeStr;
                                                                                                                                                                                                                          speakToUser(reportStr);
                                                                                                                                                                                                                      }
                                                                                                                                                                                                                      
                                                                                                                                                                                                                      

                                                                                                                                                                                                                      Now you have a complete, functioning accessibility service. Try configuring how it interacts with the user, by adding Android's text-to-speech engine, or using a Vibrator to provide haptic feedback!

                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                      Accessibility Testing Checklist | Android Developers Skip to content

                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                        Accessibility Testing Checklist

                                                                                                                                                                                                                        Testing is an important part of making your application accessible to users with varying abilities. Following design and development guidelines for accessibility are important steps toward that goal, but testing for accessibility can uncover problems with user interaction that are not obvious during design and development.

                                                                                                                                                                                                                        This accessibility testing checklist guides you through the important aspects of accessibility testing, including overall goals, required testing steps, recommended testing and special considerations. This document also discusses how to enable accessibility features on Android devices for testing purposes.

                                                                                                                                                                                                                        Testing Goals

                                                                                                                                                                                                                        Your accessibility testing should have the following, high level goals:

                                                                                                                                                                                                                        • Set up and use the application without sighted assistance
                                                                                                                                                                                                                        • All task workflows in the application can be easily navigated using directional controls and provide clear and appropriate feedback

                                                                                                                                                                                                                        Testing Requirements

                                                                                                                                                                                                                        The following tests must be completed in order to ensure a minimum level of application accessibility.

                                                                                                                                                                                                                        1. Directional controls: Verify that the application can be operated without the use of a touch screen. Attempt to use only directional controls to accomplish the primary tasks in the application. Use the keyboard and directional-pad (D-Pad) controls in the Android Emulator or use gesture navigation on devices with Android 4.1 (API Level 16) or higher.

                                                                                                                                                                                                                          Note: Keyboards and D-pads provide different navigation paths than accessibility gestures. While gestures allow users to focus on nearly any on-screen content, keyboard and D-pad navigation only allow focus on input fields and buttons.

                                                                                                                                                                                                                        2. TalkBack audio prompts: Verify that user interface controls that provide information (graphics or text) or allow user action have clear and accurate audio descriptions when TalkBack is enabled and controls are focused. Use directional controls to move focus between application layout elements.
                                                                                                                                                                                                                        3. Explore by Touch prompts: Verify that user interface controls that provide information (graphics or text) or allow user action have appropriate audio descriptions when Explore by Touch is enabled. There should be no regions where contents or controls do not provide an audio description.
                                                                                                                                                                                                                        4. Touchable control sizes: All controls where a user can select or take an action must be a minimum of 48 dp (approximately 9mm) in length and width, as recommended by Android Design.
                                                                                                                                                                                                                        5. Gestures work with TalkBack enabled: Verify that app-specific gestures, such as zooming images, scrolling lists, swiping between pages or navigating carousel controls continue to work when TalkBack is enabled. If these gestures do not function, then an alternative interface for these actions must be provided.
                                                                                                                                                                                                                        6. No audio-only feedback: Audio feedback must always have a secondary feedback mechanism to support users who are deaf or hard of hearing, for example: A sound alert for the arrival of a message should also be accompanied by a system Notification, haptic feedback (if available) or another visual alert.

                                                                                                                                                                                                                        Testing Recommendations

                                                                                                                                                                                                                        The following tests are recommended for ensuring the accessibility of your application. If you do not test these items, it may impact the overall accessibility and quality of your application.

                                                                                                                                                                                                                        1. Repetitive audio prompting: Check that closely related controls (such as items with multiple components in a list) do not simply repeat the same audio prompt. For example, in a contacts list that contains a contact picture, written name and title, the prompts should not simply repeat “Bob Smith” for each item.
                                                                                                                                                                                                                        2. Audio prompt overloading or underloading: Check that closely related controls provide an appropriate level of audio information that enables users to understand and act on a screen element. Too little or too much prompting can make it difficult to understand and use a control.

                                                                                                                                                                                                                        Special Cases and Considerations

                                                                                                                                                                                                                        The following list describes specific situations that should be tested to ensure an accessible app. Some, none or all of the cases described here may apply to your application. Be sure to review this list to find out if these special cases apply and take appropriate action.

                                                                                                                                                                                                                        1. Review developer special cases and considerations: Review the list of special cases for accessibility development and test your application for the cases that apply.
                                                                                                                                                                                                                        2. Prompts for controls that change function: Buttons or other controls that change function due to application context or workflow must provide audio prompts appropriate to their current function. For example, a button that changes function from play video to pause video should provide an audio prompt which is appropriate to its current state.
                                                                                                                                                                                                                        3. Video playback and captioning: If the application provides video playback, verify that it supports captioning and subtitles to assist users who are deaf or hard of hearing. The video playback controls must clearly indicate if captioning is available for a video and provide a clear way of enabling captions.

                                                                                                                                                                                                                        Testing Accessibility Features

                                                                                                                                                                                                                        Testing of accessibility features such as TalkBack, Explore by Touch and accessibility Gestures requires setup of your testing device. This section describes how to enable these features for accessibility testing.

                                                                                                                                                                                                                        Testing audible feedback

                                                                                                                                                                                                                        Audible accessibility feedback features on Android devices provide audio prompts that speaks the screen content as you move around an application. By enabling these features on an Android device, you can test the experience of users with blindness or low-vision using your application.

                                                                                                                                                                                                                        Audible feedback for users on Android is typically provided by TalkBack accessibility service and the Explore by Touch system feature. The TalkBack accessibility service comes preinstalled on most Android devices and can also be downloaded for free from Google Play.

                                                                                                                                                                                                                        Testing with TalkBack

                                                                                                                                                                                                                        The TalkBack accessibility service works by speaking the contents of user interface controls as the user moves focus onto controls. This service should be enabled as part of testing focus navigation and audible prompts.

                                                                                                                                                                                                                        To enable the TalkBack accessibility service:

                                                                                                                                                                                                                        1. Launch the Settings application.
                                                                                                                                                                                                                        2. Navigate to the Accessibility category and select it.
                                                                                                                                                                                                                        3. Select Accessibility to enable it.
                                                                                                                                                                                                                        4. Select TalkBack to enable it.

                                                                                                                                                                                                                        Note: While TalkBack is the most available Android accessibility service for users with disabilities, other accessibility services are available and may be installed by users.

                                                                                                                                                                                                                        For more information about using TalkBack, see TalkBack.

                                                                                                                                                                                                                        Testing with Explore by Touch

                                                                                                                                                                                                                        The Explore by Touch system feature is available on devices running Android 4.0 and later, and works by enabling a special accessibility mode that allows users to drag a finger around the interface of an application and hear the contents of the screen spoken. This feature does not require screen elements to be focused using an directional controller, but listens for hover events over user interface controls.

                                                                                                                                                                                                                        To enable Explore by Touch:

                                                                                                                                                                                                                        1. Launch the Settings application.
                                                                                                                                                                                                                        2. Navigate to the Accessibility category and select it.
                                                                                                                                                                                                                        3. Select the TalkBack to enable it.

                                                                                                                                                                                                                          Note: On Android 4.1 (API Level 16) and higher, the system provides a popup message to enable Explore by Touch. On older versions, you must follow the step below.

                                                                                                                                                                                                                        4. Return to the Accessibility category and select Explore by Touch to enable it.

                                                                                                                                                                                                                          Note: You must turn on TalkBack first, otherwise this option is not available.

                                                                                                                                                                                                                        For more information about using the Explore by Touch features, see Touch Exploration.

                                                                                                                                                                                                                        Testing focus navigation

                                                                                                                                                                                                                        Focus navigation is the use of directional controls to navigate between the individual user interface elements of an application in order to operate it. Users with limited vision or limited manual dexterity often use this mode of navigation instead of touch navigation. As part of accessibility testing, you should verify that your application can be operated using only directional controls.

                                                                                                                                                                                                                        You can test navigation of your application using only focus controls, even if your test devices does not have a directional controller. The Android Emulator provides a simulated directional controller that you can use to test navigation. You can also use a software-based directional controller, such as the one provided by the Eyes-Free Keyboard to simulate use of a D-pad on a test device that does not have a physical D-pad.

                                                                                                                                                                                                                        Testing gesture navigation

                                                                                                                                                                                                                        Gesture navigation is an accessibility navigation mode that allows users to navigate Android devices and applications using specific gestures. This navigation mode is available on Android 4.1 (API Level 16) and higher.

                                                                                                                                                                                                                        Note: Accessibility gestures provide a different navigation path than keyboards and D-pads. While gestures allow users to focus on nearly any on-screen content, keyboard and D-pad navigation only allow focus on input fields and buttons.

                                                                                                                                                                                                                        To enable gesture navigation:

                                                                                                                                                                                                                        • Enable both TalkBack and the Explore by Touch feature as described in the Testing with Explore by Touch. When both of these features are enabled, accessibility gestures are automatically enabled.
                                                                                                                                                                                                                        • You can change gesture settings using Settings > Accessibility > TalkBack > Settings > Manage shortcut gestures.

                                                                                                                                                                                                                        For more information about using Explore by Touch accessibility gestures, see Touch Exploration.

                                                                                                                                                                                                                        Note: Accessibility services other than TalkBack may map accessibility gestures to different user actions. If gestures are not producing the expected actions during testing, try disabling other accessibility services before proceeding.

                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                        Managing the System UI | Android Developers Skip to content

                                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                                        navigation

                                                                                                                                                                                                                          Managing the System UI

                                                                                                                                                                                                                          Dependencies and prerequisites

                                                                                                                                                                                                                          • Android 1.6 (API Level 4) or higher

                                                                                                                                                                                                                          You should also read

                                                                                                                                                                                                                          Try it out

                                                                                                                                                                                                                          Get the sample

                                                                                                                                                                                                                          ImmersiveMode sample

                                                                                                                                                                                                                          Design Guide

                                                                                                                                                                                                                          System Bars

                                                                                                                                                                                                                          Video

                                                                                                                                                                                                                          DevBytes: Android 4.4 Immersive Mode

                                                                                                                                                                                                                          system bars

                                                                                                                                                                                                                          Figure 1. System bars, including the [1] status bar, and [2] navigation bar.

                                                                                                                                                                                                                          The system bars are screen areas dedicated to the display of notifications, communication of device status, and device navigation. Typically the system bars (which consist of the status and navigation bars, as shown in figure 1) are displayed concurrently with your app. Apps that display immersive content, such as movies or images, can temporarily dim the system bar icons for a less distracting experience, or temporarily hide the bars for a fully immersive experience.

                                                                                                                                                                                                                          If you're familiar with the Android Design Guide, you know the importance of designing your apps to conform to standard Android UI guidelines and usage patterns. You should carefully consider your users' needs and expectations before modifying the system bars, since they give users a standard way of navigating a device and viewing its status.

                                                                                                                                                                                                                          This class describes how to dim or hide system bars across different versions of Android to create an immersive user experience, while still preserving easy access to the system bars.

                                                                                                                                                                                                                          Lessons

                                                                                                                                                                                                                          Dimming the System Bars
                                                                                                                                                                                                                          Learn how to dim the status and navigation bars.
                                                                                                                                                                                                                          Hiding the Status Bar
                                                                                                                                                                                                                          Learn how to hide the status bar on different versions of Android.
                                                                                                                                                                                                                          Hiding the Navigation Bar
                                                                                                                                                                                                                          Learn how to hide the navigation bar, in addition to the status bar.
                                                                                                                                                                                                                          Using Immersive Full-Screen Mode
                                                                                                                                                                                                                          Learn how to create a fully immersive experience in your app.
                                                                                                                                                                                                                          Responding to UI Visibility Changes
                                                                                                                                                                                                                          Learn how to register a listener to get notified of system UI visibility changes so that you can adjust your app's UI accordingly.
                                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                          Material Design for Developers | Android Developers Skip to content

                                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                                          navigation

                                                                                                                                                                                                                            Material Design for Developers

                                                                                                                                                                                                                            Dependencies and Prerequisites

                                                                                                                                                                                                                            • Android 5.0 (API level 21)

                                                                                                                                                                                                                            Material design is a comprehensive guide for visual, motion, and interaction design across platforms and devices. To use material design in your Android apps, follow the guidelines described in the material design specification and use the new components and functionality available in Android 5.0 (API level 21).

                                                                                                                                                                                                                            This class shows you how to create material design apps with the following elements:

                                                                                                                                                                                                                            • The material theme
                                                                                                                                                                                                                            • Widgets for cards and lists
                                                                                                                                                                                                                            • Custom shadows and view clipping
                                                                                                                                                                                                                            • Vector drawables
                                                                                                                                                                                                                            • Custom animations

                                                                                                                                                                                                                            This class also teaches you how to maintain compatibility with versions of Android earlier than 5.0 (API level 21) when you use material design features in your app.

                                                                                                                                                                                                                            Lessons

                                                                                                                                                                                                                            Getting Started
                                                                                                                                                                                                                            Learn how to update your app with material design features.
                                                                                                                                                                                                                            Using the Material Theme
                                                                                                                                                                                                                            Learn how to apply material design styles to your app.
                                                                                                                                                                                                                            Creating Lists and Cards
                                                                                                                                                                                                                            Learn how to create lists and cards with a consistent look and feel using system widgets.
                                                                                                                                                                                                                            Defining Shadows and Clipping Views
                                                                                                                                                                                                                            Learn how to set elevation for your views to create custom shadows and how to clip views.
                                                                                                                                                                                                                            Working with Drawables
                                                                                                                                                                                                                            Learn how to create vector drawables and how to tint drawable resources.
                                                                                                                                                                                                                            Defining Custom Animations
                                                                                                                                                                                                                            Learn how to create custom animations for views and activity transitions with shared elements.
                                                                                                                                                                                                                            Maintaining Compatibility
                                                                                                                                                                                                                            Learn how to maintain compatibility with platform versions earlier than Android 5.0.

                                                                                                                                                                                                                            Video Training

                                                                                                                                                                                                                            If you prefer to learn through interactive video training, check out this online course about material design for Android developers.

                                                                                                                                                                                                                            Start the video course

                                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                            Best Practices for User Input | Android Developers Skip to content

                                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                                            navigation

                                                                                                                                                                                                                              Best Practices for User Input

                                                                                                                                                                                                                              These classes cover various subjects of user input, such as touch screen gestures and text input through on-screen input methods and hardware keyboards.

                                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                              Using Touch Gestures | Android Developers Skip to content

                                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                                Using Touch Gestures

                                                                                                                                                                                                                                Dependencies and prerequisites

                                                                                                                                                                                                                                • Android 1.6 (API Level 4) or higher

                                                                                                                                                                                                                                You should also read

                                                                                                                                                                                                                                Try it out

                                                                                                                                                                                                                                Download the sample

                                                                                                                                                                                                                                InteractiveChart.zip

                                                                                                                                                                                                                                This class describes how to write apps that allow users to interact with an app via touch gestures. Android provides a variety of APIs to help you create and detect gestures.

                                                                                                                                                                                                                                Although your app should not depend on touch gestures for basic behaviors (since the gestures may not be available to all users in all contexts), adding touch-based interaction to your app can greatly increase its usefulness and appeal.

                                                                                                                                                                                                                                To provide users with a consistent, intuitive experience, your app should follow the accepted Android conventions for touch gestures. The Gestures design guide shows you how to use common gestures in Android apps. Also see the Design Guide for Touch Feedback.

                                                                                                                                                                                                                                Lessons

                                                                                                                                                                                                                                Detecting Common Gestures
                                                                                                                                                                                                                                Learn how to detect basic touch gestures such as scrolling, flinging, and double-tapping, using GestureDetector.
                                                                                                                                                                                                                                Tracking Movement
                                                                                                                                                                                                                                Learn how to track movement.
                                                                                                                                                                                                                                Animating a Scroll Gesture
                                                                                                                                                                                                                                Learn how to use scrollers (Scroller or OverScroller) to produce a scrolling animation in response to a touch event.
                                                                                                                                                                                                                                Handling Multi-Touch Gestures
                                                                                                                                                                                                                                Learn how to detect multi-pointer (finger) gestures.
                                                                                                                                                                                                                                Dragging and Scaling
                                                                                                                                                                                                                                Learn how to implement touch-based dragging and scaling.
                                                                                                                                                                                                                                Managing Touch Events in a ViewGroup
                                                                                                                                                                                                                                Learn how to manage touch events in a ViewGroup to ensure that touch events are correctly dispatched to their target views.
                                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                Handling Keyboard Input | Android Developers Skip to content

                                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                                  Handling Keyboard Input

                                                                                                                                                                                                                                  Dependencies and prerequisites

                                                                                                                                                                                                                                  • Android 1.6 (API Level 3) or higher

                                                                                                                                                                                                                                  The Android system shows an on-screen keyboard—known as a soft input method—when a text field in your UI receives focus. To provide the best user experience, you can specify characteristics about the type of input you expect (such as whether it's a phone number or email address) and how the input method should behave (such as whether it performs auto-correct for spelling mistakes).

                                                                                                                                                                                                                                  In addition to the on-screen input methods, Android also supports hardware keyboards, so it's important that your app optimize its user experience for interaction that might occur through an attached keyboard.

                                                                                                                                                                                                                                  These topics and more are discussed in the following lessons.

                                                                                                                                                                                                                                  Lessons

                                                                                                                                                                                                                                  Specifying the Input Method Type
                                                                                                                                                                                                                                  Learn how to show certain soft input methods, such as those designed for phone numbers, web addresses, or other formats. Also learn how to specify characteristics such as spelling suggestion behavior and action buttons such as Done or Next.
                                                                                                                                                                                                                                  Handling Input Method Visibility
                                                                                                                                                                                                                                  Learn how to specify when to show the soft input method and how your layout should adjust to the reduced screen space.
                                                                                                                                                                                                                                  Supporting Keyboard Navigation
                                                                                                                                                                                                                                  Learn how to verify that users can navigate your app using a keyboard and how to make any necessary changes to the navigation order.
                                                                                                                                                                                                                                  Handling Keyboard Actions
                                                                                                                                                                                                                                  Learn how to respond directly to keyboard input for user actions.
                                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                  Specifying the Input Method Type | Android Developers Skip to content

                                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                                    Specifying the Input Method Type

                                                                                                                                                                                                                                    Every text field expects a certain type of text input, such as an email address, phone number, or just plain text. So it's important that you specify the input type for each text field in your app so the system displays the appropriate soft input method (such as an on-screen keyboard).

                                                                                                                                                                                                                                    Beyond the type of buttons available with an input method, you should specify behaviors such as whether the input method provides spelling suggestions, capitalizes new sentences, and replaces the carriage return button with an action button such as a Done or Next. This lesson shows how to specify these characteristics.

                                                                                                                                                                                                                                    Specify the Keyboard Type

                                                                                                                                                                                                                                    You should always declare the input method for your text fields by adding the android:inputType attribute to the <EditText> element.

                                                                                                                                                                                                                                    Figure 1. The phone input type.

                                                                                                                                                                                                                                    For example, if you'd like an input method for entering a phone number, use the "phone" value:

                                                                                                                                                                                                                                    <EditText
                                                                                                                                                                                                                                        android:id="@+id/phone"
                                                                                                                                                                                                                                        android:layout_width="fill_parent"
                                                                                                                                                                                                                                        android:layout_height="wrap_content"
                                                                                                                                                                                                                                        android:hint="@string/phone_hint"
                                                                                                                                                                                                                                        android:inputType="phone" />
                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                    Figure 2. The textPassword input type.

                                                                                                                                                                                                                                    Or if the text field is for a password, use the "textPassword" value so the text field conceals the user's input:

                                                                                                                                                                                                                                    <EditText
                                                                                                                                                                                                                                        android:id="@+id/password"
                                                                                                                                                                                                                                        android:hint="@string/password_hint"
                                                                                                                                                                                                                                        android:inputType="textPassword"
                                                                                                                                                                                                                                        ... />    
                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                    There are several possible values documented with the android:inputType attribute and some of the values can be combined to specify the input method appearance and additional behaviors.

                                                                                                                                                                                                                                    Enable Spelling Suggestions and Other Behaviors

                                                                                                                                                                                                                                    Figure 3. Adding textAutoCorrect provides auto-correction for misspellings.

                                                                                                                                                                                                                                    The android:inputType attribute allows you to specify various behaviors for the input method. Most importantly, if your text field is intended for basic text input (such as for a text message), you should enable auto spelling correction with the "textAutoCorrect" value.

                                                                                                                                                                                                                                    You can combine different behaviors and input method styles with the android:inputType attribute. For example, here's how to create a text field that capitalizes the first word of a sentence and also auto-corrects misspellings:

                                                                                                                                                                                                                                    <EditText
                                                                                                                                                                                                                                        android:id="@+id/message"
                                                                                                                                                                                                                                        android:layout_width="wrap_content"
                                                                                                                                                                                                                                        android:layout_height="wrap_content"
                                                                                                                                                                                                                                        android:inputType=
                                                                                                                                                                                                                                            "textCapSentences|textAutoCorrect"
                                                                                                                                                                                                                                        ... />
                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                    Specify the Input Method Action

                                                                                                                                                                                                                                    Most soft input methods provide a user action button in the bottom corner that's appropriate for the current text field. By default, the system uses this button for either a Next or Done action unless your text field allows multi-line text (such as with android:inputType="textMultiLine"), in which case the action button is a carriage return. However, you can specify additional actions that might be more appropriate for your text field, such as Send or Go.

                                                                                                                                                                                                                                    To specify the keyboard action button, use the android:imeOptions attribute with an action value such as "actionSend" or "actionSearch". For example:

                                                                                                                                                                                                                                    Figure 4. The Send button appears when you declare android:imeOptions="actionSend".

                                                                                                                                                                                                                                    <EditText
                                                                                                                                                                                                                                        android:id="@+id/search"
                                                                                                                                                                                                                                        android:layout_width="fill_parent"
                                                                                                                                                                                                                                        android:layout_height="wrap_content"
                                                                                                                                                                                                                                        android:hint="@string/search_hint"
                                                                                                                                                                                                                                        android:inputType="text"
                                                                                                                                                                                                                                        android:imeOptions="actionSend" />
                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                    You can then listen for presses on the action button by defining a TextView.OnEditorActionListener for the EditText element. In your listener, respond to the appropriate IME action ID defined in the EditorInfo class, such as IME_ACTION_SEND. For example:

                                                                                                                                                                                                                                    EditText editText = (EditText) findViewById(R.id.search);
                                                                                                                                                                                                                                    editText.setOnEditorActionListener(new OnEditorActionListener() {
                                                                                                                                                                                                                                        @Override
                                                                                                                                                                                                                                        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                                                                                                                                                                                                                                            boolean handled = false;
                                                                                                                                                                                                                                            if (actionId == EditorInfo.IME_ACTION_SEND) {
                                                                                                                                                                                                                                                sendMessage();
                                                                                                                                                                                                                                                handled = true;
                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                            return handled;
                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                    });
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                    Handling Input Method Visibility | Android Developers Skip to content

                                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                                      Handling Input Method Visibility

                                                                                                                                                                                                                                      When input focus moves into or out of an editable text field, Android shows or hides the input method (such as the on-screen keyboard) as appropriate. The system also makes decisions about how your UI and the text field appear above the input method. For example, when the vertical space on the screen is constrained, the text field might fill all space above the input method. For most apps, these default behaviors are all that's needed.

                                                                                                                                                                                                                                      In some cases, though, you might want to more directly control the visibility of the input method and specify how you'd like your layout to appear when the input method is visible. This lesson explains how to control and respond to the input method visibility.

                                                                                                                                                                                                                                      Show the Input Method When the Activity Starts

                                                                                                                                                                                                                                      Although Android gives focus to the first text field in your layout when the activity starts, it does not show the input method. This behavior is appropriate because entering text might not be the primary task in the activity. However, if entering text is indeed the primary task (such as in a login screen), then you probably want the input method to appear by default.

                                                                                                                                                                                                                                      To show the input method when your activity starts, add the android:windowSoftInputMode attribute to the <activity> element with the "stateVisible" value. For example:

                                                                                                                                                                                                                                      <application ... >
                                                                                                                                                                                                                                          <activity
                                                                                                                                                                                                                                              android:windowSoftInputMode="stateVisible" ... >
                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                          </activity>
                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                      </application>
                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                      Note: If the user's device has an attached hardware keyboard, the soft input method does not appear.

                                                                                                                                                                                                                                      Show the Input Method On Demand

                                                                                                                                                                                                                                      If there is a method in your activity's lifecycle where you want to ensure that the input method is visible, you can use the InputMethodManager to show it.

                                                                                                                                                                                                                                      For example, the following method takes a View in which the user should type something, calls requestFocus() to give it focus, then showSoftInput() to open the input method:

                                                                                                                                                                                                                                      public void showSoftKeyboard(View view) {
                                                                                                                                                                                                                                          if (view.requestFocus()) {
                                                                                                                                                                                                                                              InputMethodManager imm = (InputMethodManager)
                                                                                                                                                                                                                                                      getSystemService(Context.INPUT_METHOD_SERVICE);
                                                                                                                                                                                                                                              imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                      Note: Once the input method is visible, you should not programmatically hide it. The system hides the input method when the user finishes the task in the text field or the user can hide it with a system control (such as with the Back button).

                                                                                                                                                                                                                                      Specify How Your UI Should Respond

                                                                                                                                                                                                                                      When the input method appears on the screen, it reduces the amount of space available for your app's UI. The system makes a decision as to how it should adjust the visible portion of your UI, but it might not get it right. To ensure the best behavior for your app, you should specify how you'd like the system to display your UI in the remaining space.

                                                                                                                                                                                                                                      To declare your preferred treatment in an activity, use the android:windowSoftInputMode attribute in your manifest's <activity> element with one of the "adjust" values.

                                                                                                                                                                                                                                      For example, to ensure that the system resizes your layout to the available space—which ensures that all of your layout content is accessible (even though it probably requires scrolling)—use "adjustResize":

                                                                                                                                                                                                                                      <application ... >
                                                                                                                                                                                                                                          <activity
                                                                                                                                                                                                                                              android:windowSoftInputMode="adjustResize" ... >
                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                          </activity>
                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                      </application>
                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                      You can combine the adjustment specification with the initial input method visibility specification from above:

                                                                                                                                                                                                                                          <activity
                                                                                                                                                                                                                                              android:windowSoftInputMode="stateVisible|adjustResize" ... >
                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                          </activity>
                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                      Specifying "adjustResize" is important if your UI includes controls that the user might need to access immediately after or while performing text input. For example, if you use a relative layout to place a button bar at the bottom of the screen, using "adjustResize" resizes the layout so the button bar appears above the input method.

                                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                      Supporting Keyboard Navigation | Android Developers Skip to content

                                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                                        Supporting Keyboard Navigation

                                                                                                                                                                                                                                        In addition to soft input methods (such as on-screen keyboards), Android supports physical keyboards attached to the device. A keyboard offers not only a convenient mode for text input, but also offers a way for users to navigate and interact with your app. Although most hand-held devices such as phones use touch as the primary mode of interaction, tablets and similar devices are growing in popularity and many users like to attach keyboard accessories.

                                                                                                                                                                                                                                        As more Android devices offer this kind of experience, it's important that you optimize your app to support interaction through a keyboard. This lesson describes how you can better support navigation with a keyboard.

                                                                                                                                                                                                                                        Note: Supporting of directional navigation in your application is also important in ensuring that your application is accessible to users who do not navigate using visual cues. Fully supporting directional navigation in your application can also help you automate user interface testing with tools like uiautomator.

                                                                                                                                                                                                                                        Test Your App

                                                                                                                                                                                                                                        It's possible that users can already navigate your app using a keyboard, because the Android system enables most of the necessary behaviors by default.

                                                                                                                                                                                                                                        All interactive widgets provided by the Android framework (such as Button and EditText) are focusable. This means users can navigate with control devices such as a D-pad or keyboard and each widget glows or otherwise changes its appearance when it gains input focus.

                                                                                                                                                                                                                                        To test your app:

                                                                                                                                                                                                                                        1. Install your app on a device that offers a hardware keyboard.

                                                                                                                                                                                                                                          If you don't have a hardware device with a keyboard, connect a Bluetooth keyboard or a USB keyboard (though not all devices support USB accessories).

                                                                                                                                                                                                                                          You can also use the Android emulator:

                                                                                                                                                                                                                                          1. In the AVD Manager, either click New Device or select an existing profile and click Clone.
                                                                                                                                                                                                                                          2. In the window that appears, ensure that Keyboard and DPad are enabled.
                                                                                                                                                                                                                                        2. To test your app, use only the Tab key to navigate through your UI, ensuring that each UI control gets focus as expected.

                                                                                                                                                                                                                                          Look for any instances in which the focus moves in a way you don't expect.

                                                                                                                                                                                                                                        3. Start from the beginning of your app and instead use the direction controls (arrow keys on the keyboard) to navigate your app.

                                                                                                                                                                                                                                          From each focusable element in your UI, press Up, Down, Left, and Right.

                                                                                                                                                                                                                                          Look for any instances in which the focus moves in a way you don't expect.

                                                                                                                                                                                                                                        If you encounter any instances where navigating with the Tab key or direction controls does not do what you expect, specify where the focus should go in your layout, as discussed in the following sections.

                                                                                                                                                                                                                                        Handle Tab Navigation

                                                                                                                                                                                                                                        When a user navigates your app using the keyboard Tab key, the system passes input focus between elements based on the order in which they appear in the layout. If you use a relative layout, for example, and the order of elements on the screen is different than the order in the file, then you might need to manually specify the focus order.

                                                                                                                                                                                                                                        For example, in the following layout, two buttons are aligned to the right side and a text field is aligned to the left of the second button. In order to pass focus from the first button to the text field, then to the second button, the layout needs to explicitly define the focus order for each of the focusable elements with the android:nextFocusForward attribute:

                                                                                                                                                                                                                                        <RelativeLayout ...>
                                                                                                                                                                                                                                            <Button
                                                                                                                                                                                                                                                android:id="@+id/button1"
                                                                                                                                                                                                                                                android:layout_alignParentTop="true"
                                                                                                                                                                                                                                                android:layout_alignParentRight="true"
                                                                                                                                                                                                                                                android:nextFocusForward="@+id/editText1"
                                                                                                                                                                                                                                                ... />
                                                                                                                                                                                                                                            <Button
                                                                                                                                                                                                                                                android:id="@+id/button2"
                                                                                                                                                                                                                                                android:layout_below="@id/button1"
                                                                                                                                                                                                                                                android:nextFocusForward="@+id/button1"
                                                                                                                                                                                                                                                ... />
                                                                                                                                                                                                                                            <EditText
                                                                                                                                                                                                                                                android:id="@id/editText1"
                                                                                                                                                                                                                                                android:layout_alignBottom="@+id/button2"
                                                                                                                                                                                                                                                android:layout_toLeftOf="@id/button2"
                                                                                                                                                                                                                                                android:nextFocusForward="@+id/button2"
                                                                                                                                                                                                                                                ...  />
                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                        </RelativeLayout>
                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                        Now instead of sending focus from button1 to button2 then editText1, the focus appropriately moves according to the appearance on the screen: from button1 to editText1 then button2.

                                                                                                                                                                                                                                        Handle Directional Navigation

                                                                                                                                                                                                                                        Users can also navigate your app using the arrow keys on a keyboard (the behavior is the same as when navigating with a D-pad or trackball). The system provides a best-guess as to which view should be given focus in a given direction based on the layout of the views on screen. Sometimes, however, the system might guess wrong.

                                                                                                                                                                                                                                        If the system does not pass focus to the appropriate view when navigating in a given direction, specify which view should receive focus with the following attributes:

                                                                                                                                                                                                                                        Each attribute designates the next view to receive focus when the user navigates in that direction, as specified by the view ID. For example:

                                                                                                                                                                                                                                        <Button
                                                                                                                                                                                                                                            android:id="@+id/button1"
                                                                                                                                                                                                                                            android:nextFocusRight="@+id/button2"
                                                                                                                                                                                                                                            android:nextFocusDown="@+id/editText1"
                                                                                                                                                                                                                                            ... />
                                                                                                                                                                                                                                        <Button
                                                                                                                                                                                                                                            android:id="@id/button2"
                                                                                                                                                                                                                                            android:nextFocusLeft="@id/button1"
                                                                                                                                                                                                                                            android:nextFocusDown="@id/editText1"
                                                                                                                                                                                                                                            ... />
                                                                                                                                                                                                                                        <EditText
                                                                                                                                                                                                                                            android:id="@id/editText1"
                                                                                                                                                                                                                                            android:nextFocusUp="@id/button1"
                                                                                                                                                                                                                                            ...  />
                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                        Handling Keyboard Actions | Android Developers Skip to content

                                                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                                                        navigation

                                                                                                                                                                                                                                          Handling Keyboard Actions

                                                                                                                                                                                                                                          This lesson teaches you to

                                                                                                                                                                                                                                          1. Handle Single Key Events
                                                                                                                                                                                                                                          2. Handle Modifier Keys

                                                                                                                                                                                                                                          When the user gives focus to an editable text view such as an EditText element and the user has a hardware keyboard attached, all input is handled by the system. If, however, you'd like to intercept or directly handle the keyboard input yourself, you can do so by implementing callback methods from the KeyEvent.Callback interface, such as onKeyDown() and onKeyMultiple().

                                                                                                                                                                                                                                          Both the Activity and View class implement the KeyEvent.Callback interface, so you should generally override the callback methods in your extension of these classes as appropriate.

                                                                                                                                                                                                                                          Note: When handling keyboard events with the KeyEvent class and related APIs, you should expect that such keyboard events come only from a hardware keyboard. You should never rely on receiving key events for any key on a soft input method (an on-screen keyboard).

                                                                                                                                                                                                                                          Handle Single Key Events

                                                                                                                                                                                                                                          To handle an individual key press, implement onKeyDown() or onKeyUp() as appropriate. Usually, you should use onKeyUp() if you want to be sure that you receive only one event. If the user presses and holds the button, then onKeyDown() is called multiple times.

                                                                                                                                                                                                                                          For example, this implementation responds to some keyboard keys to control a game:

                                                                                                                                                                                                                                          @Override
                                                                                                                                                                                                                                          public boolean onKeyUp(int keyCode, KeyEvent event) {
                                                                                                                                                                                                                                              switch (keyCode) {
                                                                                                                                                                                                                                                  case KeyEvent.KEYCODE_D:
                                                                                                                                                                                                                                                      moveShip(MOVE_LEFT);
                                                                                                                                                                                                                                                      return true;
                                                                                                                                                                                                                                                  case KeyEvent.KEYCODE_F:
                                                                                                                                                                                                                                                      moveShip(MOVE_RIGHT);
                                                                                                                                                                                                                                                      return true;
                                                                                                                                                                                                                                                  case KeyEvent.KEYCODE_J:
                                                                                                                                                                                                                                                      fireMachineGun();
                                                                                                                                                                                                                                                      return true;
                                                                                                                                                                                                                                                  case KeyEvent.KEYCODE_K:
                                                                                                                                                                                                                                                      fireMissile();
                                                                                                                                                                                                                                                      return true;
                                                                                                                                                                                                                                                  default:
                                                                                                                                                                                                                                                      return super.onKeyUp(keyCode, event);
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                          

                                                                                                                                                                                                                                          Handle Modifier Keys

                                                                                                                                                                                                                                          To respond to modifier key events such as when a key is combined with Shift or Control, you can query the KeyEvent that's passed to the callback method. Several methods provide information about modifier keys such as getModifiers() and getMetaState(). However, the simplest solution is to check whether the exact modifier key you care about is being pressed with methods such as isShiftPressed() and isCtrlPressed().

                                                                                                                                                                                                                                          For example, here's the onKeyDown() implementation again, with some extra handling for when the Shift key is held down with one of the keys:

                                                                                                                                                                                                                                          @Override
                                                                                                                                                                                                                                          public boolean onKeyUp(int keyCode, KeyEvent event) {
                                                                                                                                                                                                                                              switch (keyCode) {
                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                  case KeyEvent.KEYCODE_J:
                                                                                                                                                                                                                                                      if (event.isShiftPressed()) {
                                                                                                                                                                                                                                                          fireLaser();
                                                                                                                                                                                                                                                      } else {
                                                                                                                                                                                                                                                          fireMachineGun();
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                      return true;
                                                                                                                                                                                                                                                  case KeyEvent.KEYCODE_K:
                                                                                                                                                                                                                                                      if (event.isShiftPressed()) {
                                                                                                                                                                                                                                                          fireSeekingMissle();
                                                                                                                                                                                                                                                      } else {
                                                                                                                                                                                                                                                          fireMissile();
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                      return true;
                                                                                                                                                                                                                                                  default:
                                                                                                                                                                                                                                                      return super.onKeyUp(keyCode, event);
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                          Supporting Game Controllers | Android Developers Skip to content

                                                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                                                          navigation

                                                                                                                                                                                                                                            Supporting Game Controllers

                                                                                                                                                                                                                                            Dependencies and prerequisites

                                                                                                                                                                                                                                            • Android 2.3 (API level 9) or higher.

                                                                                                                                                                                                                                            You should also read

                                                                                                                                                                                                                                            Try it out

                                                                                                                                                                                                                                            Download the sample

                                                                                                                                                                                                                                            ControllerSample.zip

                                                                                                                                                                                                                                            You can greatly enhance the user experience in your game by letting players use their favorite game controllers. The Android framework provides APIs for detecting and processing user input from game controllers.

                                                                                                                                                                                                                                            This class shows how to make your game work consistently with game controllers across different Android API levels (API level 9 and up), and how to enhance the gaming experience for players by supporting multiple controllers simultaneously in your app.

                                                                                                                                                                                                                                            Lessons

                                                                                                                                                                                                                                            Handling Controller Actions
                                                                                                                                                                                                                                            Learn how to handle user input from common input elements on game controllers, including directional pad (D-pad) buttons, gamepad buttons, and joysticks.
                                                                                                                                                                                                                                            Supporting Controllers Across Android Versions
                                                                                                                                                                                                                                            Learn how to make game controllers behave the same across devices running different versions of Android.
                                                                                                                                                                                                                                            Supporting Multiple Game Controllers
                                                                                                                                                                                                                                            Learn how to detect and use multiple game controllers that are simultaneously connected.
                                                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                            Handling Controller Actions | Android Developers Skip to content

                                                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                                                            navigation

                                                                                                                                                                                                                                              Handling Controller Actions

                                                                                                                                                                                                                                              At the system level, Android reports input event codes from game controllers as Android key codes and axis values. In your game, you can receive these codes and values and convert them to specific in-game actions.

                                                                                                                                                                                                                                              When players physically connect or wirelessly pair a game controller to their Android-powered devices, the system auto-detects the controller as an input device and starts reporting its input events. Your game can receive these input events by implementing the following callback methods in your active Activity or focused View (you should implement the callbacks for either the Activity or View, but not both):

                                                                                                                                                                                                                                              The recommended approach is to capture the events from the specific View object that the user interacts with. Inspect the following objects provided by the callbacks to get information about the type of input event received:

                                                                                                                                                                                                                                              KeyEvent
                                                                                                                                                                                                                                              An object that describes directional pad (D-pad) and gamepad button events. Key events are accompanied by a key code that indicates the specific button triggered, such as DPAD_DOWN or BUTTON_A. You can obtain the key code by calling getKeyCode() or from key event callbacks such as onKeyDown().
                                                                                                                                                                                                                                              MotionEvent
                                                                                                                                                                                                                                              An object that describes input from joystick and shoulder trigger movements. Motion events are accompanied by an action code and a set of axis values. The action code specifies the state change that occurred such as a joystick being moved. The axis values describe the position and other movement properties for a specific physical control, such as AXIS_X or AXIS_RTRIGGER. You can obtain the action code by calling getAction() and the axis value by calling getAxisValue().

                                                                                                                                                                                                                                              This lesson focuses on how you can handle input from the most common types of physical controls (gamepad buttons, directional pads, and joysticks) in a game screen by implementing the above-mentioned View callback methods and processing KeyEvent and MotionEvent objects.

                                                                                                                                                                                                                                              Verify a Game Controller is Connected

                                                                                                                                                                                                                                              When reporting input events, Android does not distinguish between events that came from a non-game controller device and events that came from a game controller. For example, a touch screen action generates an AXIS_X event that represents the X coordinate of the touch surface, but a joystick generates an AXIS_X event that represents the X position of the joystick. If your game cares about handling game-controller input, you should first check that the input event comes from a relevant source type.

                                                                                                                                                                                                                                              To verify that a connected input device is a game controller, call getSources() to obtain a combined bit field of input source types supported on that device. You can then test to see if the following fields are set:

                                                                                                                                                                                                                                              • A source type of SOURCE_GAMEPAD indicates that the input device has gamepad buttons (for example, BUTTON_A). Note that this source type does not strictly indicate if the game controller has D-pad buttons, although most gamepads typically have directional controls.
                                                                                                                                                                                                                                              • A source type of SOURCE_DPAD indicates that the input device has D-pad buttons (for example, DPAD_UP).
                                                                                                                                                                                                                                              • A source type of SOURCE_JOYSTICK indicates that the input device has analog control sticks (for example, a joystick that records movements along AXIS_X and AXIS_Y).

                                                                                                                                                                                                                                              The following code snippet shows a helper method that lets you check whether the connected input devices are game controllers. If so, the method retrieves the device IDs for the game controllers. You can then associate each device ID with a player in your game, and process game actions for each connected player separately. To learn more about supporting multiple game controllers that are simultaneously connected on the same Android device, see Supporting Multiple Game Controllers.

                                                                                                                                                                                                                                              public ArrayList getGameControllerIds() {
                                                                                                                                                                                                                                                  ArrayList gameControllerDeviceIds = new ArrayList();
                                                                                                                                                                                                                                                  int[] deviceIds = InputDevice.getDeviceIds();
                                                                                                                                                                                                                                                  for (int deviceId : deviceIds) {
                                                                                                                                                                                                                                                      InputDevice dev = InputDevice.getDevice(deviceId);
                                                                                                                                                                                                                                                      int sources = dev.getSources();
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                      // Verify that the device has gamepad buttons, control sticks, or both.
                                                                                                                                                                                                                                                      if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
                                                                                                                                                                                                                                                              || ((sources & InputDevice.SOURCE_JOYSTICK)
                                                                                                                                                                                                                                                              == InputDevice.SOURCE_JOYSTICK)) {
                                                                                                                                                                                                                                                          // This device is a game controller. Store its device ID.
                                                                                                                                                                                                                                                          if (!gameControllerDeviceIds.contains(deviceId)) {
                                                                                                                                                                                                                                                              gameControllerDeviceIds.add(deviceId);
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  return gameControllerDeviceIds;
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                              Additionally, you might want to check for individual input capabilities supported by a connected game controller. This might be useful, for example, if you want your game to use only input from the set of physical controls it understands.

                                                                                                                                                                                                                                              To detect if a specific key code or axis code is supported by a connected game controller, use these techniques:

                                                                                                                                                                                                                                              • In Android 4.4 (API level 19) or higher, you can determine if a key code is supported on a connected game controller by calling hasKeys(int).
                                                                                                                                                                                                                                              • In Android 3.1 (API level 12) or higher, you can find all available axes supported on a connected game controller by first calling getMotionRanges(). Then, on each InputDevice.MotionRange object returned, call getAxis() to get its axis ID.

                                                                                                                                                                                                                                              Process Gamepad Button Presses

                                                                                                                                                                                                                                              Figure 1 shows how Android maps key codes and axis values to the physical controls on most game controllers.

                                                                                                                                                                                                                                              Figure 1. Profile for a generic game controller.

                                                                                                                                                                                                                                              The callouts in the figure refer to the following:

                                                                                                                                                                                                                                              Common key codes generated by gamepad button presses include BUTTON_A, BUTTON_B, BUTTON_SELECT, and BUTTON_START. Some game controllers also trigger the DPAD_CENTER key code when the center of the D-pad crossbar is pressed. Your game can inspect the key code by calling getKeyCode() or from key event callbacks such as onKeyDown(), and if it represents an event that is relevant to your game, process it as a game action. Table 1 lists the recommended game actions for the most common gamepad buttons.

                                                                                                                                                                                                                                              Table 1. Recommended game actions for gamepad buttons.

                                                                                                                                                                                                                                              Game Action Button Key Code
                                                                                                                                                                                                                                              Start game in main menu, or pause/unpause during game BUTTON_START*
                                                                                                                                                                                                                                              Display menu BUTTON_SELECT* and KEYCODE_MENU*
                                                                                                                                                                                                                                              Same as Android Back navigation behavior described in the Navigation design guide. KEYCODE_BACK
                                                                                                                                                                                                                                              Navigate back to a previous item in a menu BUTTON_B
                                                                                                                                                                                                                                              Confirm selection, or perform primary game action BUTTON_A and DPAD_CENTER

                                                                                                                                                                                                                                              * Your game should not rely on the presence of the Start, Select, or Menu buttons.

                                                                                                                                                                                                                                              Tip: Consider providing a configuration screen in your game to allow users to personalize their own game controller mappings for game actions.

                                                                                                                                                                                                                                              The following snippet shows how you might override onKeyDown() to associate the BUTTON_A and DPAD_CENTER button presses with a game action.

                                                                                                                                                                                                                                              public class GameView extends View {
                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  @Override
                                                                                                                                                                                                                                                  public boolean onKeyDown(int keyCode, KeyEvent event) {
                                                                                                                                                                                                                                                      boolean handled = false;
                                                                                                                                                                                                                                                      if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                                                                                                                                                                                                                                                              == InputDevice.SOURCE_GAMEPAD) {
                                                                                                                                                                                                                                                          if (event.getRepeatCount() == 0) {
                                                                                                                                                                                                                                                              switch (keyCode) {
                                                                                                                                                                                                                                                                  // Handle gamepad and D-pad button presses to
                                                                                                                                                                                                                                                                  // navigate the ship
                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                  default:
                                                                                                                                                                                                                                                                       if (isFireKey(keyCode)) {
                                                                                                                                                                                                                                                                           // Update the ship object to fire lasers
                                                                                                                                                                                                                                                                           ...
                                                                                                                                                                                                                                                                           handled = true;
                                                                                                                                                                                                                                                                       }
                                                                                                                                                                                                                                                                   break;
                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                          if (handled) {
                                                                                                                                                                                                                                                              return true;
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                      return super.onKeyDown(keyCode, event);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  private static boolean isFireKey(int keyCode) {
                                                                                                                                                                                                                                                      // Here we treat Button_A and DPAD_CENTER as the primary action
                                                                                                                                                                                                                                                      // keys for the game.
                                                                                                                                                                                                                                                      return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                                                                                                                                                                                                                                                              || keyCode == KeyEvent.KEYCODE_BUTTON_A;
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                              Note: On Android 4.2 (API level 17) and lower, the system treats BUTTON_A as the Android Back key by default. If your app supports these Android versions, make sure to treat BUTTON_A as the primary game action. To determine the current Android SDK version on the device, refer to the Build.VERSION.SDK_INT value.

                                                                                                                                                                                                                                              Process Directional Pad Input

                                                                                                                                                                                                                                              The 4-way directional pad (D-pad) is a common physical control in many game controllers. Android reports D-pad UP and DOWN presses as AXIS_HAT_Y events with a range from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as AXIS_HAT_X events with a range from -1.0 (left) to 1.0 (right).

                                                                                                                                                                                                                                              Some controllers instead report D-pad presses with a key code. If your game cares about D-pad presses, you should treat the hat axis events and the D-pad key codes as the same input events, as recommended in table 2.

                                                                                                                                                                                                                                              Table 2. Recommended default game actions for D-pad key codes and hat axis values.

                                                                                                                                                                                                                                              Game Action D-pad Key Code Hat Axis Code
                                                                                                                                                                                                                                              Move Up KEYCODE_DPAD_UP AXIS_HAT_Y (for values 0 to -1.0)
                                                                                                                                                                                                                                              Move Down KEYCODE_DPAD_DOWN AXIS_HAT_Y (for values 0 to 1.0)
                                                                                                                                                                                                                                              Move Left KEYCODE_DPAD_LEFT AXIS_HAT_X (for values 0 to -1.0)
                                                                                                                                                                                                                                              Move Right KEYCODE_DPAD_RIGHT AXIS_HAT_X (for values 0 to 1.0)

                                                                                                                                                                                                                                              The following code snippet shows a helper class that lets you check the hat axis and key code values from an input event to determine the D-pad direction.

                                                                                                                                                                                                                                              public class Dpad {
                                                                                                                                                                                                                                                  final static int UP       = 0;
                                                                                                                                                                                                                                                  final static int LEFT     = 1;
                                                                                                                                                                                                                                                  final static int RIGHT    = 2;
                                                                                                                                                                                                                                                  final static int DOWN     = 3;
                                                                                                                                                                                                                                                  final static int CENTER   = 4;
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  int directionPressed = -1; // initialized to -1
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  public int getDirectionPressed(InputEvent event) {
                                                                                                                                                                                                                                                      if (!isDpadDevice(event)) {
                                                                                                                                                                                                                                                         return -1;
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                      // If the input event is a MotionEvent, check its hat axis values.
                                                                                                                                                                                                                                                      if (event instanceof MotionEvent) {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                          // Use the hat axis value to find the D-pad direction
                                                                                                                                                                                                                                                          MotionEvent motionEvent = (MotionEvent) event;
                                                                                                                                                                                                                                                          float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
                                                                                                                                                                                                                                                          float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                          // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
                                                                                                                                                                                                                                                          // LEFT and RIGHT direction accordingly.
                                                                                                                                                                                                                                                          if (Float.compare(xaxis, -1.0f) == 0) {
                                                                                                                                                                                                                                                              directionPressed =  Dpad.LEFT;
                                                                                                                                                                                                                                                          } else if (Float.compare(xaxis, 1.0f) == 0) {
                                                                                                                                                                                                                                                              directionPressed =  Dpad.RIGHT;
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                          // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
                                                                                                                                                                                                                                                          // UP and DOWN direction accordingly.
                                                                                                                                                                                                                                                          else if (Float.compare(yaxis, -1.0f) == 0) {
                                                                                                                                                                                                                                                              directionPressed =  Dpad.UP;
                                                                                                                                                                                                                                                          } else if (Float.compare(yaxis, 1.0f) == 0) {
                                                                                                                                                                                                                                                              directionPressed =  Dpad.DOWN;
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                      // If the input event is a KeyEvent, check its key code.
                                                                                                                                                                                                                                                      else if (event instanceof KeyEvent) {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                         // Use the key code to find the D-pad direction.
                                                                                                                                                                                                                                                          KeyEvent keyEvent = (KeyEvent) event;
                                                                                                                                                                                                                                                          if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                                                                                                                                                                                                                                                              directionPressed = Dpad.LEFT;
                                                                                                                                                                                                                                                          } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                                                                                                                                                                                                                                                              directionPressed = Dpad.RIGHT;
                                                                                                                                                                                                                                                          } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
                                                                                                                                                                                                                                                              directionPressed = Dpad.UP;
                                                                                                                                                                                                                                                          } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
                                                                                                                                                                                                                                                              directionPressed = Dpad.DOWN;
                                                                                                                                                                                                                                                          } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                                                                                                                                                                                                                                                              directionPressed = Dpad.CENTER;
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                      return directionPressed;
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  public static boolean isDpadDevice(InputEvent event) {
                                                                                                                                                                                                                                                      // Check that input comes from a device with directional pads.
                                                                                                                                                                                                                                                      if ((event.getSource() & InputDevice.SOURCE_DPAD)
                                                                                                                                                                                                                                                           != InputDevice.SOURCE_DPAD) {
                                                                                                                                                                                                                                                           return true;
                                                                                                                                                                                                                                                       } else {
                                                                                                                                                                                                                                                           return false;
                                                                                                                                                                                                                                                       }
                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                              You can use this helper class in your game wherever you want to process D-pad input (for example, in the onGenericMotionEvent() or onKeyDown() callbacks).

                                                                                                                                                                                                                                              For example:

                                                                                                                                                                                                                                              Dpad mDpad = new Dpad();
                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                              @Override
                                                                                                                                                                                                                                              public boolean onGenericMotionEvent(MotionEvent event) {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  // Check if this event if from a D-pad and process accordingly.
                                                                                                                                                                                                                                                  if (Dpad.isDpadDevice(event)) {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                     int press = mDpad.getDirectionPressed(event);
                                                                                                                                                                                                                                                     switch (press) {
                                                                                                                                                                                                                                                          case LEFT:
                                                                                                                                                                                                                                                              // Do something for LEFT direction press
                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                              return true;
                                                                                                                                                                                                                                                          case RIGHT:
                                                                                                                                                                                                                                                              // Do something for RIGHT direction press
                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                              return true;
                                                                                                                                                                                                                                                          case UP:
                                                                                                                                                                                                                                                              // Do something for UP direction press
                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                              return true;
                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  // Check if this event is from a joystick movement and process accordingly.
                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                              Process Joystick Movements

                                                                                                                                                                                                                                              When players move a joystick on their game controllers, Android reports a MotionEvent that contains the ACTION_MOVE action code and the updated positions of the joystick's axes. Your game can use the data provided by the MotionEvent to determine if a joystick movement it cares about happened.

                                                                                                                                                                                                                                              Note that joystick motion events may batch multiple movement samples together within a single object. The MotionEvent object contains the current position for each joystick axis as well as multiple historical positions for each axis. When reporting motion events with action code ACTION_MOVE (such as joystick movements), Android batches up the axis values for efficiency. The historical values for an axis consists of the set of distinct values older than the current axis value, and more recent than values reported in any previous motion events. See the MotionEvent reference for details.

                                                                                                                                                                                                                                              You can use the historical information to more accurately render a game object's movement based on the joystick input. To retrieve the current and historical values, call getAxisValue() or getHistoricalAxisValue(). You can also find the number of historical points in the joystick event by calling getHistorySize().

                                                                                                                                                                                                                                              The following snippet shows how you might override the onGenericMotionEvent() callback to process joystick input. You should first process the historical values for an axis, then process its current position.

                                                                                                                                                                                                                                              public class GameView extends View {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  @Override
                                                                                                                                                                                                                                                  public boolean onGenericMotionEvent(MotionEvent event) {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                      // Check that the event came from a game controller
                                                                                                                                                                                                                                                      if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
                                                                                                                                                                                                                                                              InputDevice.SOURCE_JOYSTICK &&
                                                                                                                                                                                                                                                              event.getAction() == MotionEvent.ACTION_MOVE) {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                          // Process all historical movement samples in the batch
                                                                                                                                                                                                                                                          final int historySize = event.getHistorySize();
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                          // Process the movements starting from the
                                                                                                                                                                                                                                                          // earliest historical position in the batch
                                                                                                                                                                                                                                                          for (int i = 0; i < historySize; i++) {
                                                                                                                                                                                                                                                              // Process the event at historical position i
                                                                                                                                                                                                                                                              processJoystickInput(event, i);
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                          // Process the current movement sample in the batch (position -1)
                                                                                                                                                                                                                                                          processJoystickInput(event, -1);
                                                                                                                                                                                                                                                          return true;
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                      return super.onGenericMotionEvent(event);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                              Before using joystick input, you need to determine if the joystick is centered, then calculate its axis movements accordingly. Joysticks typically have a flat area, that is, a range of values near the (0,0) coordinate at which the axis is considered to be centered. If the axis value reported by Android falls within the flat area, you should treat the controller to be at rest (that is, motionless along both axes).

                                                                                                                                                                                                                                              The snippet below shows a helper method that calculates the movement along each axis. You invoke this helper in the processJoystickInput() method described further below.

                                                                                                                                                                                                                                              private static float getCenteredAxis(MotionEvent event,
                                                                                                                                                                                                                                                      InputDevice device, int axis, int historyPos) {
                                                                                                                                                                                                                                                  final InputDevice.MotionRange range =
                                                                                                                                                                                                                                                          device.getMotionRange(axis, event.getSource());
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  // A joystick at rest does not always report an absolute position of
                                                                                                                                                                                                                                                  // (0,0). Use the getFlat() method to determine the range of values
                                                                                                                                                                                                                                                  // bounding the joystick axis center.
                                                                                                                                                                                                                                                  if (range != null) {
                                                                                                                                                                                                                                                      final float flat = range.getFlat();
                                                                                                                                                                                                                                                      final float value =
                                                                                                                                                                                                                                                              historyPos < 0 ? event.getAxisValue(axis):
                                                                                                                                                                                                                                                              event.getHistoricalAxisValue(axis, historyPos);
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                      // Ignore axis values that are within the 'flat' region of the
                                                                                                                                                                                                                                                      // joystick axis center.
                                                                                                                                                                                                                                                      if (Math.abs(value) > flat) {
                                                                                                                                                                                                                                                          return value;
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  return 0;
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                              Putting it all together, here is how you might process joystick movements in your game:

                                                                                                                                                                                                                                              private void processJoystickInput(MotionEvent event,
                                                                                                                                                                                                                                                      int historyPos) {
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  InputDevice mInputDevice = event.getDevice();
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  // Calculate the horizontal distance to move by
                                                                                                                                                                                                                                                  // using the input value from one of these physical controls:
                                                                                                                                                                                                                                                  // the left control stick, hat axis, or the right control stick.
                                                                                                                                                                                                                                                  float x = getCenteredAxis(event, mInputDevice,
                                                                                                                                                                                                                                                          MotionEvent.AXIS_X, historyPos);
                                                                                                                                                                                                                                                  if (x == 0) {
                                                                                                                                                                                                                                                      x = getCenteredAxis(event, mInputDevice,
                                                                                                                                                                                                                                                              MotionEvent.AXIS_HAT_X, historyPos);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  if (x == 0) {
                                                                                                                                                                                                                                                      x = getCenteredAxis(event, mInputDevice,
                                                                                                                                                                                                                                                              MotionEvent.AXIS_Z, historyPos);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  // Calculate the vertical distance to move by
                                                                                                                                                                                                                                                  // using the input value from one of these physical controls:
                                                                                                                                                                                                                                                  // the left control stick, hat switch, or the right control stick.
                                                                                                                                                                                                                                                  float y = getCenteredAxis(event, mInputDevice,
                                                                                                                                                                                                                                                          MotionEvent.AXIS_Y, historyPos);
                                                                                                                                                                                                                                                  if (y == 0) {
                                                                                                                                                                                                                                                      y = getCenteredAxis(event, mInputDevice,
                                                                                                                                                                                                                                                              MotionEvent.AXIS_HAT_Y, historyPos);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  if (y == 0) {
                                                                                                                                                                                                                                                      y = getCenteredAxis(event, mInputDevice,
                                                                                                                                                                                                                                                              MotionEvent.AXIS_RZ, historyPos);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  // Update the ship object based on the new x and y values
                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                              To support game controllers that have more sophisticated features beyond a single joystick, follow these best practices:

                                                                                                                                                                                                                                              • Handle dual controller sticks. Many game controllers have both a left and right joystick. For the left stick, Android reports horizontal movements as AXIS_X events and vertical movements as AXIS_Y events. For the right stick, Android reports horizontal movements as AXIS_Z events and vertical movements as AXIS_RZ events. Make sure to handle both controller sticks in your code.
                                                                                                                                                                                                                                              • Handle shoulder trigger presses (but provide alternative input methods). Some controllers have left and right shoulder triggers. If these triggers are present, Android reports a left trigger press as an AXIS_LTRIGGER event and a right trigger press as an AXIS_RTRIGGER event. On Android 4.3 (API level 18), a controller that produces a AXIS_LTRIGGER also reports an identical value for the AXIS_BRAKE axis. The same is true for AXIS_RTRIGGER and AXIS_GAS. Android reports all analog trigger presses with a normalized value from 0.0 (released) to 1.0 (fully pressed). Not all controllers have triggers, so consider allowing players to perform those game actions with other buttons.
                                                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                              Supporting Controllers Across Android Versions | Android Developers Skip to content

                                                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                                                Supporting Controllers Across Android Versions

                                                                                                                                                                                                                                                If you are supporting game controllers in your game, it's your responsibility to make sure that your game responds to controllers consistently across devices running on different versions of Android. This lets your game reach a wider audience, and your players can enjoy a seamless gameplay experience with their controllers even when they switch or upgrade their Android devices.

                                                                                                                                                                                                                                                This lesson demonstrates how to use APIs available in Android 4.1 and higher in a backward compatible way, enabling your game to support the following features on devices running Android 3.1 and higher:

                                                                                                                                                                                                                                                • The game can detect if a new game controller is added, changed, or removed.
                                                                                                                                                                                                                                                • The game can query the capabilities of a game controller.
                                                                                                                                                                                                                                                • The game can recognize incoming motion events from a game controller.

                                                                                                                                                                                                                                                The examples in this lesson are based on the reference implementation provided by the sample ControllerSample.zip available for download above. This sample shows how to implement the InputManagerCompat interface to support different versions of Android. To compile the sample, you must use Android 4.1 (API level 16) or higher. Once compiled, the sample app runs on any device running Android 3.1 (API level 12) or higher as the build target.

                                                                                                                                                                                                                                                Prepare to Abstract APIs for Game Controller Support

                                                                                                                                                                                                                                                Suppose you want to be able to determine if a game controller's connection status has changed on devices running on Android 3.1 (API level 12). However, the APIs are only available in Android 4.1 (API level 16) and higher, so you need to provide an implementation that supports Android 4.1 and higher while providing a fallback mechanism that supports Android 3.1 up to Android 4.0.

                                                                                                                                                                                                                                                To help you determine which features require such a fallback mechanism for older versions, table 1 lists the differences in game controller support between Android 3.1 (API level 12) and 4.1 (API level 16).

                                                                                                                                                                                                                                                Table 1. APIs for game controller support across different Android versions.

                                                                                                                                                                                                                                                Controller Information Controller API API level 12 API level 16
                                                                                                                                                                                                                                                Device Identification getInputDeviceIds()  
                                                                                                                                                                                                                                                getInputDevice()  
                                                                                                                                                                                                                                                getVibrator()  
                                                                                                                                                                                                                                                SOURCE_JOYSTICK
                                                                                                                                                                                                                                                SOURCE_GAMEPAD
                                                                                                                                                                                                                                                Connection Status onInputDeviceAdded()  
                                                                                                                                                                                                                                                onInputDeviceChanged()  
                                                                                                                                                                                                                                                onInputDeviceRemoved()  
                                                                                                                                                                                                                                                Input Event Identification D-pad press ( KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_CENTER)
                                                                                                                                                                                                                                                Gamepad button press ( BUTTON_A, BUTTON_B, BUTTON_THUMBL, BUTTON_THUMBR, BUTTON_SELECT, BUTTON_START, BUTTON_R1, BUTTON_L1, BUTTON_R2, BUTTON_L2)
                                                                                                                                                                                                                                                Joystick and hat switch movement ( AXIS_X, AXIS_Y, AXIS_Z, AXIS_RZ, AXIS_HAT_X, AXIS_HAT_Y)
                                                                                                                                                                                                                                                Analog trigger press ( AXIS_LTRIGGER, AXIS_RTRIGGER)

                                                                                                                                                                                                                                                You can use abstraction to build version-aware game controller support that works across platforms. This approach involves the following steps:

                                                                                                                                                                                                                                                1. Define an intermediary Java interface that abstracts the implementation of the game controller features required by your game.
                                                                                                                                                                                                                                                2. Create a proxy implementation of your interface that uses APIs in Android 4.1 and higher.
                                                                                                                                                                                                                                                3. Create a custom implementation of your interface that uses APIs available between Android 3.1 up to Android 4.0.
                                                                                                                                                                                                                                                4. Create the logic for switching between these implementations at runtime, and begin using the interface in your game.

                                                                                                                                                                                                                                                For an overview of how abstraction can be used to ensure that applications can work in a backward compatible way across different versions of Android, see Creating Backward-Compatible UIs.

                                                                                                                                                                                                                                                Add an Interface for Backward Compatibility

                                                                                                                                                                                                                                                To provide backward compatibility, you can create a custom interface then add version-specific implementations. One advantage of this approach is that it lets you mirror the public interfaces on Android 4.1 (API level 16) that support game controllers.

                                                                                                                                                                                                                                                // The InputManagerCompat interface is a reference example.
                                                                                                                                                                                                                                                // The full code is provided in the ControllerSample.zip sample.
                                                                                                                                                                                                                                                public interface InputManagerCompat {
                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                    public InputDevice getInputDevice(int id);
                                                                                                                                                                                                                                                    public int[] getInputDeviceIds();
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    public void registerInputDeviceListener(
                                                                                                                                                                                                                                                            InputManagerCompat.InputDeviceListener listener,
                                                                                                                                                                                                                                                            Handler handler);
                                                                                                                                                                                                                                                    public void unregisterInputDeviceListener(
                                                                                                                                                                                                                                                            InputManagerCompat.InputDeviceListener listener);
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    public void onGenericMotionEvent(MotionEvent event);
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    public void onPause();
                                                                                                                                                                                                                                                    public void onResume();
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    public interface InputDeviceListener {
                                                                                                                                                                                                                                                        void onInputDeviceAdded(int deviceId);
                                                                                                                                                                                                                                                        void onInputDeviceChanged(int deviceId);
                                                                                                                                                                                                                                                        void onInputDeviceRemoved(int deviceId);
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                The InputManagerCompat interface provides the following methods:

                                                                                                                                                                                                                                                getInputDevice()
                                                                                                                                                                                                                                                Mirrors getInputDevice(). Obtains the InputDevice object that represents the capabilities of a game controller.
                                                                                                                                                                                                                                                getInputDeviceIds()
                                                                                                                                                                                                                                                Mirrors getInputDeviceIds(). Returns an array of integers, each of which is an ID for a different input device. This is useful if you're building a game that supports multiple players and you want to detect how many controllers are connected.
                                                                                                                                                                                                                                                registerInputDeviceListener()
                                                                                                                                                                                                                                                Mirrors registerInputDeviceListener(). Lets you register to be informed when a new device is added, changed, or removed.
                                                                                                                                                                                                                                                unregisterInputDeviceListener()
                                                                                                                                                                                                                                                Mirrors unregisterInputDeviceListener(). Unregisters an input device listener.
                                                                                                                                                                                                                                                onGenericMotionEvent()
                                                                                                                                                                                                                                                Mirrors onGenericMotionEvent(). Lets your game intercept and handle MotionEvent objects and axis values that represent events such as joystick movements and analog trigger presses.
                                                                                                                                                                                                                                                onPause()
                                                                                                                                                                                                                                                Stops polling for game controller events when the main activity is paused, or when the game no longer has focus.
                                                                                                                                                                                                                                                onResume()
                                                                                                                                                                                                                                                Starts polling for game controller events when the main activity is resumed, or when the game is started and runs in the foreground.
                                                                                                                                                                                                                                                InputDeviceListener
                                                                                                                                                                                                                                                Mirrors the InputManager.InputDeviceListener interface. Lets your game know when a game controller has been added, changed, or removed.

                                                                                                                                                                                                                                                Next, create implementations for InputManagerCompat that work across different platform versions. If your game is running on Android 4.1 or higher and calls an InputManagerCompat method, the proxy implementation calls the equivalent method in InputManager. However, if your game is running on Android 3.1 up to Android 4.0, the custom implementation processes calls to InputManagerCompat methods by using only APIs introduced no later than Android 3.1. Regardless of which version-specific implementation is used at runtime, the implementation passes the call results back transparently to the game.

                                                                                                                                                                                                                                                Figure 1. Class diagram of interface and version-specific implementations.

                                                                                                                                                                                                                                                Implement the Interface on Android 4.1 and Higher

                                                                                                                                                                                                                                                InputManagerCompatV16 is an implementation of the InputManagerCompat interface that proxies method calls to an actual InputManager and InputManager.InputDeviceListener. The InputManager is obtained from the system Context.

                                                                                                                                                                                                                                                // The InputManagerCompatV16 class is a reference implementation.
                                                                                                                                                                                                                                                // The full code is provided in the ControllerSample.zip sample.
                                                                                                                                                                                                                                                public class InputManagerV16 implements InputManagerCompat {
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    private final InputManager mInputManager;
                                                                                                                                                                                                                                                    private final Map mListeners;
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    public InputManagerV16(Context context) {
                                                                                                                                                                                                                                                        mInputManager = (InputManager)
                                                                                                                                                                                                                                                                context.getSystemService(Context.INPUT_SERVICE);
                                                                                                                                                                                                                                                        mListeners = new HashMap();
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public InputDevice getInputDevice(int id) {
                                                                                                                                                                                                                                                        return mInputManager.getInputDevice(id);
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public int[] getInputDeviceIds() {
                                                                                                                                                                                                                                                        return mInputManager.getInputDeviceIds();
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    static class V16InputDeviceListener implements
                                                                                                                                                                                                                                                            InputManager.InputDeviceListener {
                                                                                                                                                                                                                                                        final InputManagerCompat.InputDeviceListener mIDL;
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                        public V16InputDeviceListener(InputDeviceListener idl) {
                                                                                                                                                                                                                                                            mIDL = idl;
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                        @Override
                                                                                                                                                                                                                                                        public void onInputDeviceAdded(int deviceId) {
                                                                                                                                                                                                                                                            mIDL.onInputDeviceAdded(deviceId);
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                        // Do the same for device change and removal
                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public void registerInputDeviceListener(InputDeviceListener listener,
                                                                                                                                                                                                                                                            Handler handler) {
                                                                                                                                                                                                                                                        V16InputDeviceListener v16Listener = new
                                                                                                                                                                                                                                                                V16InputDeviceListener(listener);
                                                                                                                                                                                                                                                        mInputManager.registerInputDeviceListener(v16Listener, handler);
                                                                                                                                                                                                                                                        mListeners.put(listener, v16Listener);
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    // Do the same for unregistering an input device listener
                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public void onGenericMotionEvent(MotionEvent event) {
                                                                                                                                                                                                                                                        // unused in V16
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public void onPause() {
                                                                                                                                                                                                                                                        // unused in V16
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public void onResume() {
                                                                                                                                                                                                                                                        // unused in V16
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                Implementing the Interface on Android 3.1 up to Android 4.0

                                                                                                                                                                                                                                                To create an implementation of InputManagerCompat that supports Android 3.1 up to Android 4.0, you can use the following objects:

                                                                                                                                                                                                                                                • A SparseArray of device IDs to track the game controllers that are connected to the device.
                                                                                                                                                                                                                                                • A Handler to process device events. When an app is started or resumed, the Handler receives a message to start polling for game controller disconnection. The Handler will start a loop to check each known connected game controller and see if a device ID is returned. A null return value indicates that the game controller is disconnected. The Handler stops polling when the app is paused.
                                                                                                                                                                                                                                                • A Map of InputManagerCompat.InputDeviceListener objects. You will use the listeners to update the connection status of tracked game controllers.
                                                                                                                                                                                                                                                // The InputManagerCompatV9 class is a reference implementation.
                                                                                                                                                                                                                                                // The full code is provided in the ControllerSample.zip sample.
                                                                                                                                                                                                                                                public class InputManagerV9 implements InputManagerCompat {
                                                                                                                                                                                                                                                    private final SparseArray mDevices;
                                                                                                                                                                                                                                                    private final Map mListeners;
                                                                                                                                                                                                                                                    private final Handler mDefaultHandler;
                                                                                                                                                                                                                                                    …
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    public InputManagerV9() {
                                                                                                                                                                                                                                                        mDevices = new SparseArray();
                                                                                                                                                                                                                                                        mListeners = new HashMap();
                                                                                                                                                                                                                                                        mDefaultHandler = new PollingMessageHandler(this);
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                Implement a PollingMessageHandler object that extends Handler, and override the handleMessage() method. This method checks if an attached game controller has been disconnected and notifies registered listeners.

                                                                                                                                                                                                                                                private static class PollingMessageHandler extends Handler {
                                                                                                                                                                                                                                                    private final WeakReference mInputManager;
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    PollingMessageHandler(InputManagerV9 im) {
                                                                                                                                                                                                                                                        mInputManager = new WeakReference(im);
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public void handleMessage(Message msg) {
                                                                                                                                                                                                                                                        super.handleMessage(msg);
                                                                                                                                                                                                                                                        switch (msg.what) {
                                                                                                                                                                                                                                                            case MESSAGE_TEST_FOR_DISCONNECT:
                                                                                                                                                                                                                                                                InputManagerV9 imv = mInputManager.get();
                                                                                                                                                                                                                                                                if (null != imv) {
                                                                                                                                                                                                                                                                    long time = SystemClock.elapsedRealtime();
                                                                                                                                                                                                                                                                    int size = imv.mDevices.size();
                                                                                                                                                                                                                                                                    for (int i = 0; i < size; i++) {
                                                                                                                                                                                                                                                                        long[] lastContact = imv.mDevices.valueAt(i);
                                                                                                                                                                                                                                                                        if (null != lastContact) {
                                                                                                                                                                                                                                                                            if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
                                                                                                                                                                                                                                                                                // check to see if the device has been
                                                                                                                                                                                                                                                                                // disconnected
                                                                                                                                                                                                                                                                                int id = imv.mDevices.keyAt(i);
                                                                                                                                                                                                                                                                                if (null == InputDevice.getDevice(id)) {
                                                                                                                                                                                                                                                                                    // Notify the registered listeners
                                                                                                                                                                                                                                                                                    // that the game controller is disconnected
                                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                                    imv.mDevices.remove(id);
                                                                                                                                                                                                                                                                                } else {
                                                                                                                                                                                                                                                                                    lastContact[0] = time;
                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                    sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
                                                                                                                                                                                                                                                                            CHECK_ELAPSED_TIME);
                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                break;
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                To start and stop polling for game controller disconnection, override these methods:

                                                                                                                                                                                                                                                private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
                                                                                                                                                                                                                                                private static final long CHECK_ELAPSED_TIME = 3000L;
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                public void onPause() {
                                                                                                                                                                                                                                                    mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                public void onResume() {
                                                                                                                                                                                                                                                    mDefaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
                                                                                                                                                                                                                                                            CHECK_ELAPSED_TIME);
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                To detect that an input device has been added, override the onGenericMotionEvent() method. When the system reports a motion event, check if this event came from a device ID that is already tracked, or from a new device ID. If the device ID is new, notify registered listeners.

                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                public void onGenericMotionEvent(MotionEvent event) {
                                                                                                                                                                                                                                                    // detect new devices
                                                                                                                                                                                                                                                    int id = event.getDeviceId();
                                                                                                                                                                                                                                                    long[] timeArray = mDevices.get(id);
                                                                                                                                                                                                                                                    if (null == timeArray) {
                                                                                                                                                                                                                                                        // Notify the registered listeners that a game controller is added
                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                        timeArray = new long[1];
                                                                                                                                                                                                                                                        mDevices.put(id, timeArray);
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                    long time = SystemClock.elapsedRealtime();
                                                                                                                                                                                                                                                    timeArray[0] = time;
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                Notification of listeners is implemented by using the Handler object to send a DeviceEvent Runnable object to the message queue. The DeviceEvent contains a reference to an InputManagerCompat.InputDeviceListener. When the DeviceEvent runs, the appropriate callback method of the listener is called to signal if the game controller was added, changed, or removed.

                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                public void registerInputDeviceListener(InputDeviceListener listener,
                                                                                                                                                                                                                                                        Handler handler) {
                                                                                                                                                                                                                                                    mListeners.remove(listener);
                                                                                                                                                                                                                                                    if (handler == null) {
                                                                                                                                                                                                                                                        handler = mDefaultHandler;
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                    mListeners.put(listener, handler);
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                public void unregisterInputDeviceListener(InputDeviceListener listener) {
                                                                                                                                                                                                                                                    mListeners.remove(listener);
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                private void notifyListeners(int why, int deviceId) {
                                                                                                                                                                                                                                                    // the state of some device has changed
                                                                                                                                                                                                                                                    if (!mListeners.isEmpty()) {
                                                                                                                                                                                                                                                        for (InputDeviceListener listener : mListeners.keySet()) {
                                                                                                                                                                                                                                                            Handler handler = mListeners.get(listener);
                                                                                                                                                                                                                                                            DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId,
                                                                                                                                                                                                                                                                    listener);
                                                                                                                                                                                                                                                            handler.post(odc);
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                private static class DeviceEvent implements Runnable {
                                                                                                                                                                                                                                                    private int mMessageType;
                                                                                                                                                                                                                                                    private int mId;
                                                                                                                                                                                                                                                    private InputDeviceListener mListener;
                                                                                                                                                                                                                                                    private static Queue sObjectQueue =
                                                                                                                                                                                                                                                            new ArrayDeque();
                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    static DeviceEvent getDeviceEvent(int messageType, int id,
                                                                                                                                                                                                                                                            InputDeviceListener listener) {
                                                                                                                                                                                                                                                        DeviceEvent curChanged = sObjectQueue.poll();
                                                                                                                                                                                                                                                        if (null == curChanged) {
                                                                                                                                                                                                                                                            curChanged = new DeviceEvent();
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                        curChanged.mMessageType = messageType;
                                                                                                                                                                                                                                                        curChanged.mId = id;
                                                                                                                                                                                                                                                        curChanged.mListener = listener;
                                                                                                                                                                                                                                                        return curChanged;
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                    public void run() {
                                                                                                                                                                                                                                                        switch (mMessageType) {
                                                                                                                                                                                                                                                            case ON_DEVICE_ADDED:
                                                                                                                                                                                                                                                                mListener.onInputDeviceAdded(mId);
                                                                                                                                                                                                                                                                break;
                                                                                                                                                                                                                                                            case ON_DEVICE_CHANGED:
                                                                                                                                                                                                                                                                mListener.onInputDeviceChanged(mId);
                                                                                                                                                                                                                                                                break;
                                                                                                                                                                                                                                                            case ON_DEVICE_REMOVED:
                                                                                                                                                                                                                                                                mListener.onInputDeviceRemoved(mId);
                                                                                                                                                                                                                                                                break;
                                                                                                                                                                                                                                                            default:
                                                                                                                                                                                                                                                                // Handle unknown message type
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                break;
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                        // Put this runnable back in the queue
                                                                                                                                                                                                                                                        sObjectQueue.offer(this);
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                You now have two implementations of InputManagerCompat: one that works on devices running Android 4.1 and higher, and another that works on devices running Android 3.1 up to Android 4.0.

                                                                                                                                                                                                                                                Use the Version-Specific Implementation

                                                                                                                                                                                                                                                The version-specific switching logic is implemented in a class that acts as a factory.

                                                                                                                                                                                                                                                public static class Factory {
                                                                                                                                                                                                                                                    public static InputManagerCompat getInputManager(Context context) {
                                                                                                                                                                                                                                                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                                                                                                                                                                                                                                                            return new InputManagerV16(context);
                                                                                                                                                                                                                                                        } else {
                                                                                                                                                                                                                                                            return new InputManagerV9();
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                Now you can simply instantiate an InputManagerCompat object and register an InputManagerCompat.InputDeviceListener in your main View. Because of the version-switching logic you set up, your game automatically uses the implementation that's appropriate for the version of Android the device is running.

                                                                                                                                                                                                                                                public class GameView extends View implements InputDeviceListener {
                                                                                                                                                                                                                                                    private InputManagerCompat mInputManager;
                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    public GameView(Context context, AttributeSet attrs) {
                                                                                                                                                                                                                                                        mInputManager =
                                                                                                                                                                                                                                                                InputManagerCompat.Factory.getInputManager(this.getContext());
                                                                                                                                                                                                                                                        mInputManager.registerInputDeviceListener(this, null);
                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                Next, override the onGenericMotionEvent() method in your main view, as described in Handle a MotionEvent from a Game Controller. Your game should now be able to process game controller events consistently on devices running Android 3.1 (API level 12) and higher.

                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                public boolean onGenericMotionEvent(MotionEvent event) {
                                                                                                                                                                                                                                                    mInputManager.onGenericMotionEvent(event);
                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                    // Handle analog input from the controller as normal
                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                    return super.onGenericMotionEvent(event);
                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                You can find a complete implementation of this compatibility code in the GameView class provided in the sample ControllerSample.zip available for download above.

                                                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                Supporting Multiple Game Controllers | Android Developers Skip to content

                                                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                                                  Supporting Multiple Game Controllers

                                                                                                                                                                                                                                                  This lesson teaches you to

                                                                                                                                                                                                                                                  1. Map Players to Controller Device IDs
                                                                                                                                                                                                                                                  2. Process Multiple Controller Input

                                                                                                                                                                                                                                                  Try it out

                                                                                                                                                                                                                                                  Download the sample

                                                                                                                                                                                                                                                  ControllerSample.zip

                                                                                                                                                                                                                                                  While most games are designed to support a single user per Android device, it's also possible to support multiple users with game controllers that are connected simultaneously on the same Android device.

                                                                                                                                                                                                                                                  This lesson covers some basic techniques for handling input in your single device multiplayer game from multiple connected controllers. This includes maintaining a mapping between player avatars and each controller device and processing controller input events appropriately.

                                                                                                                                                                                                                                                  Map Players to Controller Device IDs

                                                                                                                                                                                                                                                  When a game controller is connected to an Android device, the system assigns it an integer device ID. You can obtain the device IDs for connected game controllers by calling InputDevice.getDeviceIds(), as shown in Verify a Game Controller is Connected. You can then associate each device ID with a player in your game, and process game actions for each player separately.

                                                                                                                                                                                                                                                  Note: On devices running Android 4.1 (API level 16) and higher, you can obtain an input device’s descriptor using getDescriptor(), which returns a unique persistent string value for the input device. Unlike a device ID, the descriptor value won't change even if the input device is disconnected, reconnected, or reconfigured.

                                                                                                                                                                                                                                                  The code snippet below shows how to use a SparseArray to associate a player's avatar with a specific controller. In this example, the mShips variable stores a collection of Ship objects. A new player avatar is created in-game when a new controller is attached by a user, and removed when its associated controller is removed.

                                                                                                                                                                                                                                                  The onInputDeviceAdded() and onInputDeviceRemoved() callback methods are part of the abstraction layer introduced in Supporting Controllers Across Android Versions. By implementing these listener callbacks, your game can identify the game controller's device ID when a controller is added or removed. This detection is compatible with Android 2.3 (API level 9) and higher.

                                                                                                                                                                                                                                                  private final SparseArray<Ship> mShips = new SparseArray<Ship>();
                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                  @Override
                                                                                                                                                                                                                                                  public void onInputDeviceAdded(int deviceId) {
                                                                                                                                                                                                                                                      getShipForID(deviceId);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                  @Override
                                                                                                                                                                                                                                                  public void onInputDeviceRemoved(int deviceId) {
                                                                                                                                                                                                                                                      removeShipForID(deviceId);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                  private Ship getShipForID(int shipID) {
                                                                                                                                                                                                                                                      Ship currentShip = mShips.get(shipID);
                                                                                                                                                                                                                                                      if ( null == currentShip ) {
                                                                                                                                                                                                                                                          currentShip = new Ship();
                                                                                                                                                                                                                                                          mShips.append(shipID, currentShip);
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                      return currentShip;
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                  private void removeShipForID(int shipID) {
                                                                                                                                                                                                                                                      mShips.remove(shipID);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                  Process Multiple Controller Input

                                                                                                                                                                                                                                                  Your game should execute the following loop to process input from multiple controllers:

                                                                                                                                                                                                                                                  1. Detect whether an input event occurred.
                                                                                                                                                                                                                                                  2. Identify the input source and its device ID.
                                                                                                                                                                                                                                                  3. Based on the action indicated by the input event key code or axis value, update the player avatar associated with that device ID.
                                                                                                                                                                                                                                                  4. Render and update the user interface.

                                                                                                                                                                                                                                                  KeyEvent and MotionEvent input events have device IDs associated with them. Your game can take advantage of this to determine which controller the input event came from, and update the player avatar associated with that controller.

                                                                                                                                                                                                                                                  The following code snippet shows how you might get a player avatar reference corresponding to a game controller device ID, and update the game based on the user's button press on that controller.

                                                                                                                                                                                                                                                  @Override
                                                                                                                                                                                                                                                  public boolean onKeyDown(int keyCode, KeyEvent event) {
                                                                                                                                                                                                                                                      if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                                                                                                                                                                                                                                                                  == InputDevice.SOURCE_GAMEPAD) {
                                                                                                                                                                                                                                                          int deviceId = event.getDeviceId();
                                                                                                                                                                                                                                                          if (deviceId != -1) {
                                                                                                                                                                                                                                                              Ship currentShip = getShipForId(deviceId);
                                                                                                                                                                                                                                                              // Based on which key was pressed, update the player avatar
                                                                                                                                                                                                                                                              // (e.g. set the ship headings or fire lasers)
                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                              return true;
                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                      return super.onKeyDown(keyCode, event);
                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                  Note: As a best practice, when a user's game controller disconnects, you should pause the game and ask if the user wants to reconnect.

                                                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                  Best Practices for Background Jobs | Android Developers Skip to content

                                                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                                                    Best Practices for Background Jobs

                                                                                                                                                                                                                                                    These classes show you how to run jobs in the background to boost your application's performance and minimize its drain on the battery.

                                                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                    Running in a Background Service | Android Developers Skip to content

                                                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                                                      Running in a Background Service

                                                                                                                                                                                                                                                      Dependencies and prerequisites

                                                                                                                                                                                                                                                      • Android 1.6 (API Level 4) or higher

                                                                                                                                                                                                                                                      You should also read

                                                                                                                                                                                                                                                      Try it out

                                                                                                                                                                                                                                                      Download the sample

                                                                                                                                                                                                                                                      ThreadSample.zip

                                                                                                                                                                                                                                                      Unless you specify otherwise, most of the operations you do in an app run in the foreground on a special thread called the UI thread. This can cause problems, because long-running operations will interfere with the responsiveness of your user interface. This annoys your users, and can even cause system errors. To avoid this, the Android framework offers several classes that help you off-load operations onto a separate thread running in the background. The most useful of these is IntentService.

                                                                                                                                                                                                                                                      This class describes how to implement an IntentService, send it work requests, and report its results to other components.

                                                                                                                                                                                                                                                      Lessons

                                                                                                                                                                                                                                                      Creating a Background Service
                                                                                                                                                                                                                                                      Learn how to create an IntentService.
                                                                                                                                                                                                                                                      Sending Work Requests to the Background Service
                                                                                                                                                                                                                                                      Learn how to send work requests to an IntentService.
                                                                                                                                                                                                                                                      Reporting Work Status
                                                                                                                                                                                                                                                      Learn how to use an Intent and a LocalBroadcastManager to communicate the status of a work request from an IntentService to the Activity that sent the request.
                                                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                      Creating a Background Service | Android Developers Skip to content

                                                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                                                        Creating a Background Service

                                                                                                                                                                                                                                                        The IntentService class provides a straightforward structure for running an operation on a single background thread. This allows it to handle long-running operations without affecting your user interface's responsiveness. Also, an IntentService isn't affected by most user interface lifecycle events, so it continues to run in circumstances that would shut down an AsyncTask

                                                                                                                                                                                                                                                        An IntentService has a few limitations:

                                                                                                                                                                                                                                                        • It can't interact directly with your user interface. To put its results in the UI, you have to send them to an Activity.
                                                                                                                                                                                                                                                        • Work requests run sequentially. If an operation is running in an IntentService, and you send it another request, the request waits until the first operation is finished.
                                                                                                                                                                                                                                                        • An operation running on an IntentService can't be interrupted.

                                                                                                                                                                                                                                                        However, in most cases an IntentService is the preferred way to perform simple background operations.

                                                                                                                                                                                                                                                        This lesson shows you how to create your own subclass of IntentService. The lesson also shows you how to create the required callback method onHandleIntent(). Finally, the lesson describes shows you how to define the IntentService in your manifest file.

                                                                                                                                                                                                                                                        Create an IntentService

                                                                                                                                                                                                                                                        To create an IntentService component for your app, define a class that extends IntentService, and within it, define a method that overrides onHandleIntent(). For example:

                                                                                                                                                                                                                                                        public class RSSPullService extends IntentService {
                                                                                                                                                                                                                                                            @Override
                                                                                                                                                                                                                                                            protected void onHandleIntent(Intent workIntent) {
                                                                                                                                                                                                                                                                // Gets data from the incoming Intent
                                                                                                                                                                                                                                                                String dataString = workIntent.getDataString();
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                // Do work here, based on the contents of dataString
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                        Notice that the other callbacks of a regular Service component, such as onStartCommand() are automatically invoked by IntentService. In an IntentService, you should avoid overriding these callbacks.

                                                                                                                                                                                                                                                        Define the IntentService in the Manifest

                                                                                                                                                                                                                                                        An IntentService also needs an entry in your application manifest. Provide this entry as a <service> element that's a child of the <application> element:

                                                                                                                                                                                                                                                            <application
                                                                                                                                                                                                                                                                android:icon="@drawable/icon"
                                                                                                                                                                                                                                                                android:label="@string/app_name">
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                <!--
                                                                                                                                                                                                                                                                    Because android:exported is set to "false",
                                                                                                                                                                                                                                                                    the service is only available to this app.
                                                                                                                                                                                                                                                                -->
                                                                                                                                                                                                                                                                <service
                                                                                                                                                                                                                                                                    android:name=".RSSPullService"
                                                                                                                                                                                                                                                                    android:exported="false"/>
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                            <application/>
                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                        The attribute android:name specifies the class name of the IntentService.

                                                                                                                                                                                                                                                        Notice that the <service> element doesn't contain an intent filter. The Activity that sends work requests to the service uses an explicit Intent, so no filter is needed. This also means that only components in the same app or other applications with the same user ID can access the service.

                                                                                                                                                                                                                                                        Now that you have the basic IntentService class, you can send work requests to it with Intent objects. The procedure for constructing these objects and sending them to your IntentService is described in the next lesson.

                                                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                        Sending Work Requests to the Background Service | Android Developers Skip to content

                                                                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                                                                        navigation

                                                                                                                                                                                                                                                          Sending Work Requests to the Background Service

                                                                                                                                                                                                                                                          This lesson teaches you to

                                                                                                                                                                                                                                                          1. Create and Send a Work Request to an IntentService

                                                                                                                                                                                                                                                          You should also read

                                                                                                                                                                                                                                                          Try it out

                                                                                                                                                                                                                                                          Download the sample

                                                                                                                                                                                                                                                          ThreadSample.zip

                                                                                                                                                                                                                                                          The previous lesson showed you how to create an IntentService class. This lesson shows you how to trigger the IntentService to run an operation by sending it an Intent. This Intent can optionally contain data for the IntentService to process. You can send an Intent to an IntentService from any point in an Activity or Fragment

                                                                                                                                                                                                                                                          Create and Send a Work Request to an IntentService

                                                                                                                                                                                                                                                          To create a work request and send it to an IntentService, create an explicit Intent, add work request data to it, and send it to IntentService by calling startService().

                                                                                                                                                                                                                                                          The next snippets demonstrate this:

                                                                                                                                                                                                                                                          1. Create a new, explicit Intent for the IntentService called RSSPullService.
                                                                                                                                                                                                                                                            /*
                                                                                                                                                                                                                                                             * Creates a new Intent to start the RSSPullService
                                                                                                                                                                                                                                                             * IntentService. Passes a URI in the
                                                                                                                                                                                                                                                             * Intent's "data" field.
                                                                                                                                                                                                                                                             */
                                                                                                                                                                                                                                                            mServiceIntent = new Intent(getActivity(), RSSPullService.class);
                                                                                                                                                                                                                                                            mServiceIntent.setData(Uri.parse(dataUrl));
                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                          2. Call startService()
                                                                                                                                                                                                                                                            // Starts the IntentService
                                                                                                                                                                                                                                                            getActivity().startService(mServiceIntent);
                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                          Notice that you can send the work request from anywhere in an Activity or Fragment. For example, if you need to get user input first, you can send the request from a callback that responds to a button click or similar gesture.

                                                                                                                                                                                                                                                          Once you call startService(), the IntentService does the work defined in its onHandleIntent() method, and then stops itself.

                                                                                                                                                                                                                                                          The next step is to report the results of the work request back to the originating Activity or Fragment. The next lesson shows you how to do this with a BroadcastReceiver.

                                                                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                          Reporting Work Status | Android Developers Skip to content

                                                                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                                                                          navigation

                                                                                                                                                                                                                                                            Reporting Work Status

                                                                                                                                                                                                                                                            This lesson teaches you to

                                                                                                                                                                                                                                                            1. Report Status From an IntentService
                                                                                                                                                                                                                                                            2. Receive Status Broadcasts from an IntentService

                                                                                                                                                                                                                                                            You should also read

                                                                                                                                                                                                                                                            Try it out

                                                                                                                                                                                                                                                            Download the sample

                                                                                                                                                                                                                                                            ThreadSample.zip

                                                                                                                                                                                                                                                            This lesson shows you how to report the status of a work request run in a background service to the component that sent the request. This allows you, for example, to report the status of the request in an Activity object's UI. The recommended way to send and receive status is to use a LocalBroadcastManager, which limits broadcast Intent objects to components in your own app.

                                                                                                                                                                                                                                                            Report Status From an IntentService

                                                                                                                                                                                                                                                            To send the status of a work request in an IntentService to other components, first create an Intent that contains the status in its extended data. As an option, you can add an action and data URI to this Intent.

                                                                                                                                                                                                                                                            Next, send the Intent by calling LocalBroadcastManager.sendBroadcast(). This sends the Intent to any component in your application that has registered to receive it. To get an instance of LocalBroadcastManager, call getInstance().

                                                                                                                                                                                                                                                            For example:

                                                                                                                                                                                                                                                            public final class Constants {
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                // Defines a custom Intent action
                                                                                                                                                                                                                                                                public static final String BROADCAST_ACTION =
                                                                                                                                                                                                                                                                    "com.example.android.threadsample.BROADCAST";
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                // Defines the key for the status "extra" in an Intent
                                                                                                                                                                                                                                                                public static final String EXTENDED_DATA_STATUS =
                                                                                                                                                                                                                                                                    "com.example.android.threadsample.STATUS";
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                            public class RSSPullService extends IntentService {
                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                /*
                                                                                                                                                                                                                                                                 * Creates a new Intent containing a Uri object
                                                                                                                                                                                                                                                                 * BROADCAST_ACTION is a custom Intent action
                                                                                                                                                                                                                                                                 */
                                                                                                                                                                                                                                                                Intent localIntent =
                                                                                                                                                                                                                                                                        new Intent(Constants.BROADCAST_ACTION)
                                                                                                                                                                                                                                                                        // Puts the status into the Intent
                                                                                                                                                                                                                                                                        .putExtra(Constants.EXTENDED_DATA_STATUS, status);
                                                                                                                                                                                                                                                                // Broadcasts the Intent to receivers in this app.
                                                                                                                                                                                                                                                                LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                            The next step is to handle the incoming broadcast Intent objects in the component that sent the original work request.

                                                                                                                                                                                                                                                            Receive Status Broadcasts from an IntentService

                                                                                                                                                                                                                                                            To receive broadcast Intent objects, use a subclass of BroadcastReceiver. In the subclass, implement the BroadcastReceiver.onReceive() callback method, which LocalBroadcastManager invokes when it receives an Intent. LocalBroadcastManager passes the incoming Intent to BroadcastReceiver.onReceive().

                                                                                                                                                                                                                                                            For example:

                                                                                                                                                                                                                                                            // Broadcast receiver for receiving status updates from the IntentService
                                                                                                                                                                                                                                                            private class ResponseReceiver extends BroadcastReceiver
                                                                                                                                                                                                                                                            {
                                                                                                                                                                                                                                                                // Prevents instantiation
                                                                                                                                                                                                                                                                private DownloadStateReceiver() {
                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                // Called when the BroadcastReceiver gets an Intent it's registered to receive
                                                                                                                                                                                                                                                                @
                                                                                                                                                                                                                                                                public void onReceive(Context context, Intent intent) {
                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                    /*
                                                                                                                                                                                                                                                                     * Handle Intents here.
                                                                                                                                                                                                                                                                     */
                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                            Once you've defined the BroadcastReceiver, you can define filters for it that match specific actions, categories, and data. To do this, create an IntentFilter. This first snippet shows how to define the filter:

                                                                                                                                                                                                                                                            // Class that displays photos
                                                                                                                                                                                                                                                            public class DisplayActivity extends FragmentActivity {
                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                public void onCreate(Bundle stateBundle) {
                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                    super.onCreate(stateBundle);
                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                    // The filter's action is BROADCAST_ACTION
                                                                                                                                                                                                                                                                    IntentFilter mStatusIntentFilter = new IntentFilter(
                                                                                                                                                                                                                                                                            Constants.BROADCAST_ACTION);
                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                    // Adds a data filter for the HTTP scheme
                                                                                                                                                                                                                                                                    mStatusIntentFilter.addDataScheme("http");
                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                            To register the BroadcastReceiver and the IntentFilter with the system, get an instance of LocalBroadcastManager and call its registerReceiver() method. This next snippet shows how to register the BroadcastReceiver and its IntentFilter:

                                                                                                                                                                                                                                                                    // Instantiates a new DownloadStateReceiver
                                                                                                                                                                                                                                                                    DownloadStateReceiver mDownloadStateReceiver =
                                                                                                                                                                                                                                                                            new DownloadStateReceiver();
                                                                                                                                                                                                                                                                    // Registers the DownloadStateReceiver and its intent filters
                                                                                                                                                                                                                                                                    LocalBroadcastManager.getInstance(this).registerReceiver(
                                                                                                                                                                                                                                                                            mDownloadStateReceiver,
                                                                                                                                                                                                                                                                            mStatusIntentFilter);
                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                            A single BroadcastReceiver can handle more than one type of broadcast Intent object, each with its own action. This feature allows you to run different code for each action, without having to define a separate BroadcastReceiver for each action. To define another IntentFilter for the same BroadcastReceiver, create the IntentFilter and repeat the call to registerReceiver(). For example:

                                                                                                                                                                                                                                                                    /*
                                                                                                                                                                                                                                                                     * Instantiates a new action filter.
                                                                                                                                                                                                                                                                     * No data filter is needed.
                                                                                                                                                                                                                                                                     */
                                                                                                                                                                                                                                                                    statusIntentFilter = new IntentFilter(Constants.ACTION_ZOOM_IMAGE);
                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                    // Registers the receiver with the new filter
                                                                                                                                                                                                                                                                    LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
                                                                                                                                                                                                                                                                            mDownloadStateReceiver,
                                                                                                                                                                                                                                                                            mIntentFilter);
                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                            Sending an broadcast Intent doesn't start or resume an Activity. The BroadcastReceiver for an Activity receives and processes Intent objects even when your app is in the background, but doesn't force your app to the foreground. If you want to notify the user about an event that happened in the background while your app was not visible, use a Notification. Never start an Activity in response to an incoming broadcast Intent.

                                                                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                            Loading Data in the Background | Android Developers Skip to content

                                                                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                                                                            navigation

                                                                                                                                                                                                                                                              Loading Data in the Background

                                                                                                                                                                                                                                                              Dependencies and prerequisites

                                                                                                                                                                                                                                                              • Android 1.6 or later

                                                                                                                                                                                                                                                              You should also read

                                                                                                                                                                                                                                                              Try it out

                                                                                                                                                                                                                                                              Download the sample

                                                                                                                                                                                                                                                              ThreadSample.zip

                                                                                                                                                                                                                                                              Querying a ContentProvider for data you want to display takes time. If you run the query directly from an Activity, it may get blocked and cause the system to issue an "Application Not Responding" message. Even if it doesn't, users will see an annoying delay in the UI. To avoid these problems, you should initiate a query on a separate thread, wait for it to finish, and then display the results.

                                                                                                                                                                                                                                                              You can do this in a straightforward way by using an object that runs a query asynchronously in the background and reconnects to your Activity when it's finished. This object is a CursorLoader. Besides doing the initial background query, a CursorLoader automatically re-runs the query when data associated with the query changes.

                                                                                                                                                                                                                                                              This class describes how to use a CursorLoader to run a background query. Examples in this class use the v4 Support Library versions of classes, which support platforms starting with Android 1.6.

                                                                                                                                                                                                                                                              Lessons

                                                                                                                                                                                                                                                              Running a Query with a CursorLoader
                                                                                                                                                                                                                                                              Learn how to run a query in the background, using a CursorLoader.
                                                                                                                                                                                                                                                              Handling the Results
                                                                                                                                                                                                                                                              Learn how to handle the Cursor returned from the query, and how to remove references to the current Cursor when the loader framework re-sets the CursorLoader.
                                                                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                              Managing Device Awake State | Android Developers Skip to content

                                                                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                                                                Managing Device Awake State

                                                                                                                                                                                                                                                                Dependencies and prerequisites

                                                                                                                                                                                                                                                                • Android 1.6 (API Level 4) or higher

                                                                                                                                                                                                                                                                Try it out

                                                                                                                                                                                                                                                                Download the sample

                                                                                                                                                                                                                                                                Scheduler.zip

                                                                                                                                                                                                                                                                When an Android device is left idle, it will first dim, then turn off the screen, and ultimately turn off the CPU. This prevents the device's battery from quickly getting drained. Yet there are times when your application might require a different behavior:

                                                                                                                                                                                                                                                                • Apps such as games or movie apps may need to keep the screen turned on.

                                                                                                                                                                                                                                                                • Other applications may not need the screen to remain on, but they may require the CPU to keep running until a critical operation finishes.

                                                                                                                                                                                                                                                                This class describes how to keep a device awake when necessary without draining its battery.

                                                                                                                                                                                                                                                                Lessons

                                                                                                                                                                                                                                                                Keeping the Device Awake
                                                                                                                                                                                                                                                                Learn how to keep the screen or CPU awake as needed, while minimizing the impact on battery life.
                                                                                                                                                                                                                                                                Scheduling Repeating Alarms
                                                                                                                                                                                                                                                                Learn how to use repeating alarms to schedule operations that take place outside of the lifetime of the application, even if the application is not running and/or the device is asleep.
                                                                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                Best Practices for Performance | Android Developers Skip to content

                                                                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                                                                  Best Practices for Performance

                                                                                                                                                                                                                                                                  These classes and articles help you build an app that's smooth, responsive, and uses as little battery as possible.

                                                                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                  Improving Layout Performance | Android Developers Skip to content

                                                                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                                                                    Improving Layout Performance

                                                                                                                                                                                                                                                                    Dependencies and prerequisites

                                                                                                                                                                                                                                                                    • Android 1.5 (API Level 3) or higher

                                                                                                                                                                                                                                                                    You should also read

                                                                                                                                                                                                                                                                    Video

                                                                                                                                                                                                                                                                    DevBytes: Optimising Layouts with Hierarchy Viewer

                                                                                                                                                                                                                                                                    Layouts are a key part of Android applications that directly affect the user experience. If implemented poorly, your layout can lead to a memory hungry application with slow UIs. The Android SDK includes tools to help you identify problems in your layout performance, which when combined the lessons here, you will be able to implement smooth scrolling interfaces with a minimum memory footprint.

                                                                                                                                                                                                                                                                    Lessons

                                                                                                                                                                                                                                                                    Optimizing Layout Hierarchies
                                                                                                                                                                                                                                                                    In the same way a complex web page can slow down load time, your layout hierarchy if too complex can also cause performance problems. This lesson shows how you can use SDK tools to inspect your layout and discover performance bottlenecks.
                                                                                                                                                                                                                                                                    Re-using Layouts with <include/>
                                                                                                                                                                                                                                                                    If your application UI repeats certain layout constructs in multiple places, this lesson shows you how to create efficient, re-usable layout constructs, then include them in the appropriate UI layouts.
                                                                                                                                                                                                                                                                    Loading Views On Demand
                                                                                                                                                                                                                                                                    Beyond simply including one layout component within another layout, you might want to make the included layout visible only when it's needed, sometime after the activity is running. This lesson shows how you can improve your layout's initialization performance by loading portions of your layout on demand.
                                                                                                                                                                                                                                                                    Making ListView Scrolling Smooth
                                                                                                                                                                                                                                                                    If you've built an instance of ListView that contains complex or data-heavy content in each list item, the scroll performance of the list might suffer. This lesson provides some tips about how you can make your scrolling performance more smooth.
                                                                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                    Optimizing Layout Hierarchies | Android Developers Skip to content

                                                                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                                                                      Optimizing Layout Hierarchies

                                                                                                                                                                                                                                                                      This lesson teaches you to

                                                                                                                                                                                                                                                                      1. Inspect Your Layout
                                                                                                                                                                                                                                                                      2. Revise Your Layout
                                                                                                                                                                                                                                                                      3. Use Lint

                                                                                                                                                                                                                                                                      You should also read

                                                                                                                                                                                                                                                                      It is a common misconception that using the basic layout structures leads to the most efficient layouts. However, each widget and layout you add to your application requires initialization, layout, and drawing. For example, using nested instances of LinearLayout can lead to an excessively deep view hierarchy. Furthermore, nesting several instances of LinearLayout that use the layout_weight parameter can be especially expensive as each child needs to be measured twice. This is particularly important when the layout is inflated repeatedly, such as when used in a ListView or GridView.

                                                                                                                                                                                                                                                                      In this lesson you'll learn to use Hierarchy Viewer and Layoutopt to examine and optimize your layout.

                                                                                                                                                                                                                                                                      Inspect Your Layout

                                                                                                                                                                                                                                                                      The Android SDK tools include a tool called Hierarchy Viewer that allows you to analyze your layout while your application is running. Using this tool helps you discover bottlenecks in the layout performance.

                                                                                                                                                                                                                                                                      Hierarchy Viewer works by allowing you to select running processes on a connected device or emulator, then display the layout tree. The traffic lights on each block represent its Measure, Layout and Draw performance, helping you identify potential issues.

                                                                                                                                                                                                                                                                      For example, figure 1 shows a layout that's used as an item in a ListView. This layout shows a small bitmap image on the left and two stacked items of text on the right. It is especially important that layouts that will be inflated multiple times—such as this one—are optimized as the performance benefits will be multiplied.

                                                                                                                                                                                                                                                                      Figure 1. Conceptual layout for an item in a ListView.

                                                                                                                                                                                                                                                                      The hierarchyviewer tool is available in <sdk>/tools/. When opened, the Hierarchy Viewer shows a list of available devices and its running components. Click Load View Hierarchy to view the layout hierarchy of the selected component. For example, figure 2 shows the layout for the list item illustrated by figure 1.

                                                                                                                                                                                                                                                                      Figure 2. Layout hierarchy for the layout in figure 1, using nested instances of LinearLayout.

                                                                                                                                                                                                                                                                      Figure 3. Clicking a hierarchy node shows its performance times.

                                                                                                                                                                                                                                                                      In figure 2, you can see there is a 3-level hierarchy with some problems laying out the text items. Clicking on the items shows the time taken for each stage of the process (figure 3). It becomes clear which items are taking the longest to measure, layout, and render, and where you should spend time optimizing.

                                                                                                                                                                                                                                                                      The timings for rendering a complete list item using this layout are:

                                                                                                                                                                                                                                                                      • Measure: 0.977ms
                                                                                                                                                                                                                                                                      • Layout: 0.167ms
                                                                                                                                                                                                                                                                      • Draw: 2.717ms

                                                                                                                                                                                                                                                                      Revise Your Layout

                                                                                                                                                                                                                                                                      Because the layout performance above slows down due to a nested LinearLayout, the performance might improve by flattening the layout—make the layout shallow and wide, rather than narrow and deep. A RelativeLayout as the root node allows for such layouts. So, when this design is converted to use RelativeLayout, you can see that the layout becomes a 2-level hierarchy. Inspection of the new layout looks like this:

                                                                                                                                                                                                                                                                      Figure 4. Layout hierarchy for the layout in figure 1, using RelativeLayout.

                                                                                                                                                                                                                                                                      Now rendering a list item takes:

                                                                                                                                                                                                                                                                      • Measure: 0.598ms
                                                                                                                                                                                                                                                                      • Layout: 0.110ms
                                                                                                                                                                                                                                                                      • Draw: 2.146ms

                                                                                                                                                                                                                                                                      Might seem like a small improvement, but this time is multiplied several times because this layout is used for every item in a list.

                                                                                                                                                                                                                                                                      Most of this time difference is due to the use of layout_weight in the LinearLayout design, which can slow down the speed of measurement. It is just one example of how each layout has appropriate uses and you should carefully consider whether using layout weight is necessary.

                                                                                                                                                                                                                                                                      Use Lint

                                                                                                                                                                                                                                                                      It is always good practice to run the lint tool on your layout files to search for possible view hierarchy optimizations. Lint has replaced the Layoutopt tool and has much greater functionality. Some examples of lint rules are:

                                                                                                                                                                                                                                                                      • Use compound drawables - A LinearLayout which contains an ImageView and a TextView can be more efficiently handled as a compound drawable.
                                                                                                                                                                                                                                                                      • Merge root frame - If a FrameLayout is the root of a layout and does not provide background or padding etc, it can be replaced with a merge tag which is slightly more efficient.
                                                                                                                                                                                                                                                                      • Useless leaf - A layout that has no children or no background can often be removed (since it is invisible) for a flatter and more efficient layout hierarchy.
                                                                                                                                                                                                                                                                      • Useless parent - A layout with children that has no siblings, is not a ScrollView or a root layout, and does not have a background, can be removed and have its children moved directly into the parent for a flatter and more efficient layout hierarchy.
                                                                                                                                                                                                                                                                      • Deep layouts - Layouts with too much nesting are bad for performance. Consider using flatter layouts such as RelativeLayout or GridLayout to improve performance. The default maximum depth is 10.

                                                                                                                                                                                                                                                                      Another benefit of Lint is that it is integrated into Android Studio. Lint automatically runs whenever you compile your program. With Android Studio, you can also run lint inspections for a specific build variant, or for all build variants.

                                                                                                                                                                                                                                                                      You can also manage inspection profiles and configure inspections within Android Studio with the File>Settings>Project Settings option. The Inspection Configuration page appears with the supported inspections.

                                                                                                                                                                                                                                                                      Figure 5. Inspection Configuration

                                                                                                                                                                                                                                                                      Lint has the ability to automatically fix some issues, provide suggestions for others and jump directly to the offending code for review.

                                                                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                      Re-using Layouts with <include/> | Android Developers Skip to content

                                                                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                                                                        Re-using Layouts with <include/>

                                                                                                                                                                                                                                                                        Although Android offers a variety of widgets to provide small and re-usable interactive elements, you might also need to re-use larger components that require a special layout. To efficiently re-use complete layouts, you can use the <include/> and <merge/> tags to embed another layout inside the current layout.

                                                                                                                                                                                                                                                                        Reusing layouts is particularly powerful as it allows you create reusable complex layouts. For example, a yes/no button panel, or custom progress bar with description text. It also means that any elements of your application that are common across multiple layouts can be extracted, managed separately, then included in each layout. So while you can create individual UI components by writing a custom View, you can do it even more easily by re-using a layout file.

                                                                                                                                                                                                                                                                        Create a Re-usable Layout

                                                                                                                                                                                                                                                                        If you already know the layout that you want to re-use, create a new XML file and define the layout. For example, here's a layout from the G-Kenya codelab that defines a title bar to be included in each activity (titlebar.xml):

                                                                                                                                                                                                                                                                        <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                            android:layout_width="match_parent"
                                                                                                                                                                                                                                                                            android:layout_height="wrap_content"
                                                                                                                                                                                                                                                                            android:background="@color/titlebar_bg">
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                            <ImageView android:layout_width="wrap_content"
                                                                                                                                                                                                                                                                                       android:layout_height="wrap_content"
                                                                                                                                                                                                                                                                                       android:src="@drawable/gafricalogo" />
                                                                                                                                                                                                                                                                        </FrameLayout>
                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                        The root View should be exactly how you'd like it to appear in each layout to which you add this layout.

                                                                                                                                                                                                                                                                        Use the <include> Tag

                                                                                                                                                                                                                                                                        Inside the layout to which you want to add the re-usable component, add the <include/> tag. For example, here's a layout from the G-Kenya codelab that includes the title bar from above:

                                                                                                                                                                                                                                                                        Here's the layout file:

                                                                                                                                                                                                                                                                        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                            android:orientation="vertical"
                                                                                                                                                                                                                                                                            android:layout_width="match_parent"
                                                                                                                                                                                                                                                                            android:layout_height="match_parent"
                                                                                                                                                                                                                                                                            android:background="@color/app_bg"
                                                                                                                                                                                                                                                                            android:gravity="center_horizontal">
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                            <include layout="@layout/titlebar"/>
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                            <TextView android:layout_width="match_parent"
                                                                                                                                                                                                                                                                                      android:layout_height="wrap_content"
                                                                                                                                                                                                                                                                                      android:text="@string/hello"
                                                                                                                                                                                                                                                                                      android:padding="10dp" />
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                        </LinearLayout>
                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                        You can also override all the layout parameters (any android:layout_* attributes) of the included layout's root view by specifying them in the <include/> tag. For example:

                                                                                                                                                                                                                                                                        <include android:id="@+id/news_title"
                                                                                                                                                                                                                                                                                 android:layout_width="match_parent"
                                                                                                                                                                                                                                                                                 android:layout_height="match_parent"
                                                                                                                                                                                                                                                                                 layout="@layout/title"/>
                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                        However, if you want to override layout attributes using the <include> tag, you must override both android:layout_height and android:layout_width in order for other layout attributes to take effect.

                                                                                                                                                                                                                                                                        Use the <merge> Tag

                                                                                                                                                                                                                                                                        The <merge /> tag helps eliminate redundant view groups in your view hierarchy when including one layout within another. For example, if your main layout is a vertical LinearLayout in which two consecutive views can be re-used in multiple layouts, then the re-usable layout in which you place the two views requires its own root view. However, using another LinearLayout as the root for the re-usable layout would result in a vertical LinearLayout inside a vertical LinearLayout. The nested LinearLayout serves no real purpose other than to slow down your UI performance.

                                                                                                                                                                                                                                                                        To avoid including such a redundant view group, you can instead use the <merge> element as the root view for the re-usable layout. For example:

                                                                                                                                                                                                                                                                        <merge xmlns:android="http://schemas.android.com/apk/res/android">
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                            <Button
                                                                                                                                                                                                                                                                                android:layout_width="fill_parent"
                                                                                                                                                                                                                                                                                android:layout_height="wrap_content"
                                                                                                                                                                                                                                                                                android:text="@string/add"/>
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                            <Button
                                                                                                                                                                                                                                                                                android:layout_width="fill_parent"
                                                                                                                                                                                                                                                                                android:layout_height="wrap_content"
                                                                                                                                                                                                                                                                                android:text="@string/delete"/>
                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                        </merge>
                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                        Now, when you include this layout in another layout (using the <include/> tag), the system ignores the <merge> element and places the two buttons directly in the layout, in place of the <include/> tag.

                                                                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                        Loading Views On Demand | Android Developers Skip to content

                                                                                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                                                                                        navigation

                                                                                                                                                                                                                                                                          Loading Views On Demand

                                                                                                                                                                                                                                                                          This lesson teaches you to

                                                                                                                                                                                                                                                                          1. Define a ViewStub
                                                                                                                                                                                                                                                                          2. Load the ViewStub Layout

                                                                                                                                                                                                                                                                          You should also read

                                                                                                                                                                                                                                                                          Sometimes your layout might require complex views that are rarely used. Whether they are item details, progress indicators, or undo messages, you can reduce memory usage and speed up rendering by loading the views only when they are needed.

                                                                                                                                                                                                                                                                          Define a ViewStub

                                                                                                                                                                                                                                                                          ViewStub is a lightweight view with no dimension and doesn’t draw anything or participate in the layout. As such, it's cheap to inflate and cheap to leave in a view hierarchy. Each ViewStub simply needs to include the android:layout attribute to specify the layout to inflate.

                                                                                                                                                                                                                                                                          The following ViewStub is for a translucent progress bar overlay. It should be visible only when new items are being imported into the application.

                                                                                                                                                                                                                                                                          <ViewStub
                                                                                                                                                                                                                                                                              android:id="@+id/stub_import"
                                                                                                                                                                                                                                                                              android:inflatedId="@+id/panel_import"
                                                                                                                                                                                                                                                                              android:layout="@layout/progress_overlay"
                                                                                                                                                                                                                                                                              android:layout_width="fill_parent"
                                                                                                                                                                                                                                                                              android:layout_height="wrap_content"
                                                                                                                                                                                                                                                                              android:layout_gravity="bottom" />
                                                                                                                                                                                                                                                                          

                                                                                                                                                                                                                                                                          Load the ViewStub Layout

                                                                                                                                                                                                                                                                          When you want to load the layout specified by the ViewStub, either set it visible by calling setVisibility(View.VISIBLE) or call inflate().

                                                                                                                                                                                                                                                                          ((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
                                                                                                                                                                                                                                                                          // or
                                                                                                                                                                                                                                                                          View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
                                                                                                                                                                                                                                                                          

                                                                                                                                                                                                                                                                          Note: The inflate() method returns the inflated View once complete. so you don't need to call findViewById() if you need to interact with the layout.

                                                                                                                                                                                                                                                                          Once visible/inflated, the ViewStub element is no longer part of the view hierarchy. It is replaced by the inflated layout and the ID for the root view of that layout is the one specified by the android:inflatedId attribute of the ViewStub. (The ID android:id specified for the ViewStub is valid only until the ViewStub layout is visible/inflated.)

                                                                                                                                                                                                                                                                          Note: One drawback of ViewStub is that it doesn’t currently support the <merge> tag in the layouts to be inflated.

                                                                                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                          Making ListView Scrolling Smooth | Android Developers Skip to content

                                                                                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                                                                                          navigation

                                                                                                                                                                                                                                                                            Making ListView Scrolling Smooth

                                                                                                                                                                                                                                                                            The key to a smoothly scrolling ListView is to keep the application’s main thread (the UI thread) free from heavy processing. Ensure you do any disk access, network access, or SQL access in a separate thread. To test the status of your app, you can enable StrictMode.

                                                                                                                                                                                                                                                                            Use a Background Thread

                                                                                                                                                                                                                                                                            Using a background thread ("worker thread") removes strain from the main thread so it can focus on drawing the UI. In many cases, using AsyncTask provides a simple way to perform your work outside the main thread. AsyncTask automatically queues up all the execute() requests and performs them serially. This behavior is global to a particular process and means you don’t need to worry about creating your own thread pool.

                                                                                                                                                                                                                                                                            In the sample code below, an AsyncTask is used to load images in a background thread, then apply them to the UI once finished. It also shows a progress spinner in place of the images while they are loading.

                                                                                                                                                                                                                                                                            // Using an AsyncTask to load the slow images in a background thread
                                                                                                                                                                                                                                                                            new AsyncTask<ViewHolder, Void, Bitmap>() {
                                                                                                                                                                                                                                                                                private ViewHolder v;
                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                                                protected Bitmap doInBackground(ViewHolder... params) {
                                                                                                                                                                                                                                                                                    v = params[0];
                                                                                                                                                                                                                                                                                    return mFakeImageLoader.getImage();
                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                @Override
                                                                                                                                                                                                                                                                                protected void onPostExecute(Bitmap result) {
                                                                                                                                                                                                                                                                                    super.onPostExecute(result);
                                                                                                                                                                                                                                                                                    if (v.position == position) {
                                                                                                                                                                                                                                                                                        // If this item hasn't been recycled already, hide the
                                                                                                                                                                                                                                                                                        // progress and set and show the image
                                                                                                                                                                                                                                                                                        v.progress.setVisibility(View.GONE);
                                                                                                                                                                                                                                                                                        v.icon.setVisibility(View.VISIBLE);
                                                                                                                                                                                                                                                                                        v.icon.setImageBitmap(result);
                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                            }.execute(holder);
                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                            Beginning with Android 3.0 (API level 11), an extra feature is available in AsyncTask so you can enable it to run across multiple processor cores. Instead of calling execute() you can specify executeOnExecutor() and multiple requests can be executed at the same time depending on the number of cores available.

                                                                                                                                                                                                                                                                            Hold View Objects in a View Holder

                                                                                                                                                                                                                                                                            Your code might call findViewById() frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById() is to use the "view holder" design pattern.

                                                                                                                                                                                                                                                                            A ViewHolder object stores each of the component views inside the tag field of the Layout, so you can immediately access them without the need to look them up repeatedly. First, you need to create a class to hold your exact set of views. For example:

                                                                                                                                                                                                                                                                            static class ViewHolder {
                                                                                                                                                                                                                                                                              TextView text;
                                                                                                                                                                                                                                                                              TextView timestamp;
                                                                                                                                                                                                                                                                              ImageView icon;
                                                                                                                                                                                                                                                                              ProgressBar progress;
                                                                                                                                                                                                                                                                              int position;
                                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                            Then populate the ViewHolder and store it inside the layout.

                                                                                                                                                                                                                                                                            ViewHolder holder = new ViewHolder();
                                                                                                                                                                                                                                                                            holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
                                                                                                                                                                                                                                                                            holder.text = (TextView) convertView.findViewById(R.id.listitem_text);
                                                                                                                                                                                                                                                                            holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp);
                                                                                                                                                                                                                                                                            holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner);
                                                                                                                                                                                                                                                                            convertView.setTag(holder);
                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                            Now you can easily access each view without the need for the look-up, saving valuable processor cycles.

                                                                                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                            Optimizing Battery Life | Android Developers Skip to content

                                                                                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                                                                                            navigation

                                                                                                                                                                                                                                                                              Optimizing Battery Life

                                                                                                                                                                                                                                                                              Dependencies and prerequisites

                                                                                                                                                                                                                                                                              You should also read

                                                                                                                                                                                                                                                                              For your app to be a good citizen, it should seek to limit its impact on the battery life of its device. After this class you will be able to build apps that modify their functionality and behavior based on the state of its device.

                                                                                                                                                                                                                                                                              By taking steps such as batching network requests, disabling background service updates when you lose connectivity, or reducing the rate of such updates when the battery level is low, you can ensure that the impact of your app on battery life is minimized, without compromising the user experience.

                                                                                                                                                                                                                                                                              Lessons

                                                                                                                                                                                                                                                                              Reducing Network Battery Drain
                                                                                                                                                                                                                                                                              Learn how to analyze your app's use of network resources and optimize it to reduce power consumption.
                                                                                                                                                                                                                                                                              Optimizing for Doze and App Standby
                                                                                                                                                                                                                                                                              Learn how to test and optimize your app for the power-management features introduced in Android 6.0 Marshmallow.
                                                                                                                                                                                                                                                                              Monitoring the Battery Level and Charging State
                                                                                                                                                                                                                                                                              Learn how to alter your app's update rate by determining, and monitoring, the current battery level and changes in charging state.
                                                                                                                                                                                                                                                                              Determining and Monitoring the Docking State and Type
                                                                                                                                                                                                                                                                              Optimal refresh rates can vary based on how the host device is being used. Learn how to determine, and monitor, the docking state and type of dock being used to affect your app's behavior.
                                                                                                                                                                                                                                                                              Determining and Monitoring the Connectivity Status
                                                                                                                                                                                                                                                                              Without Internet connectivity you can't update your app from an online source. Learn how to check the connectivity status to alter your background update rate. You'll also learn to check for Wi-Fi or mobile connectivity before beginning high-bandwidth operations.
                                                                                                                                                                                                                                                                              Manipulating Broadcast Receivers On Demand
                                                                                                                                                                                                                                                                              Broadcast receivers that you've declared in the manifest can be toggled at runtime to disable those that aren't necessary due to the current device state. Learn to improve efficiency by toggling and cascading state change receivers and delay actions until the device is in a specific state.
                                                                                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                              Reducing Network Battery Drain | Android Developers Skip to content

                                                                                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                                                                                Reducing Network Battery Drain

                                                                                                                                                                                                                                                                                Requests that your app makes to the network are a major cause of battery drain because they turn on the power-hungry mobile or Wi-Fi radios. Beyond the power needed to send and receive packets, these radios expend extra power just turning on and keeping awake. Something as simple as a network request every 15 seconds can keep the mobile radio on continuously and quickly use up battery power.

                                                                                                                                                                                                                                                                                This lesson shows you how to tag your app's source code to categorize, visualize and color your network requests according to how they are initiated. From there, each category identifies areas of your app that you can make more battery-efficient.

                                                                                                                                                                                                                                                                                Performance Actions

                                                                                                                                                                                                                                                                                Collecting Network Traffic Data
                                                                                                                                                                                                                                                                                Learn how to instrument your app's code and gather data on its use of network resources.
                                                                                                                                                                                                                                                                                Analyzing Network Traffic Data
                                                                                                                                                                                                                                                                                Learn how to analyze your app's use of network resources in response to user actions and optimize it to reduce power consumption.
                                                                                                                                                                                                                                                                                Optimizing User-Initiated Network Use
                                                                                                                                                                                                                                                                                Learn how to optimize your app's use of network resources in response to user actions to reduce power consumption.
                                                                                                                                                                                                                                                                                Optimizing App-Initiated Network Use
                                                                                                                                                                                                                                                                                Learn how to optimize your app's requests for network resources to reduce power consumption.
                                                                                                                                                                                                                                                                                Optimizing Server-Initiated Network Use
                                                                                                                                                                                                                                                                                Learn how to optimize your app's requests for network resources and to reduce power consumption.
                                                                                                                                                                                                                                                                                Optimizing General Network Use
                                                                                                                                                                                                                                                                                Learn how to optimize your app's requests for network resources and to reduce power consumption.
                                                                                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                Optimizing for Doze and App Standby | Android Developers Skip to content

                                                                                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                                                                                  Optimizing for Doze and App Standby

                                                                                                                                                                                                                                                                                  Starting from Android 6.0 (API level 23), Android introduces two power-saving features that extend battery life for users by managing how apps behave when a device is not connected to a power source. Doze reduces battery consumption by deferring background CPU and network activity for apps when the device is unused for long periods of time. App Standby defers background network activity for apps with which the user has not recently interacted.

                                                                                                                                                                                                                                                                                  Doze and App Standby manage the behavior of all apps running on Android 6.0 or higher, regardless whether they are specifically targeting API level 23. To ensure the best experience for users, test your app in Doze and App Standby modes and make any necessary adjustments to your code. The sections below provide details.

                                                                                                                                                                                                                                                                                  Understanding Doze

                                                                                                                                                                                                                                                                                  If a user leaves a device unplugged and stationary for a period of time, with the screen off, the device enters Doze mode. In Doze mode, the system attempts to conserve battery by restricting apps' access to network and CPU-intensive services. It also prevents apps from accessing the network and defers their jobs, syncs, and standard alarms.

                                                                                                                                                                                                                                                                                  Periodically, the system exits Doze for a brief time to let apps complete their deferred activities. During this maintenance window, the system runs all pending syncs, jobs, and alarms, and lets apps access the network.

                                                                                                                                                                                                                                                                                  Figure 1. Doze provides a recurring maintenance window for apps to use the network and handle pending activities.

                                                                                                                                                                                                                                                                                  At the conclusion of each maintenance window, the system again enters Doze, suspending network access and deferring jobs, syncs, and alarms. Over time, the system schedules maintenance windows less and less frequently, helping to reduce battery consumption in cases of longer-term inactivity when the device is not connected to a charger.

                                                                                                                                                                                                                                                                                  As soon as the user wakes the device by moving it, turning on the screen, or connecting a charger, the system exits Doze and all apps return to normal activity.

                                                                                                                                                                                                                                                                                  Doze restrictions

                                                                                                                                                                                                                                                                                  The following restrictions apply to your apps while in Doze:

                                                                                                                                                                                                                                                                                  Doze checklist

                                                                                                                                                                                                                                                                                  Adapting your app to Doze

                                                                                                                                                                                                                                                                                  Doze can affect apps differently, depending on the capabilities they offer and the services they use. Many apps function normally across Doze cycles without modification. In some cases, you must optimize the way that your app manages network, alarms, jobs, and syncs. Apps should be able to efficiently manage activities during each maintenance window.

                                                                                                                                                                                                                                                                                  Doze is particularly likely to affect activities that AlarmManager alarms and timers manage, because alarms in Android 5.1 (API level 22) or lower do not fire when the system is in Doze.

                                                                                                                                                                                                                                                                                  To help with scheduling alarms, Android 6.0 (API level 23) introduces two new AlarmManager methods: setAndAllowWhileIdle() and setExactAndAllowWhileIdle(). With these methods, you can set alarms that will fire even if the device is in Doze.

                                                                                                                                                                                                                                                                                  Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app.

                                                                                                                                                                                                                                                                                  The Doze restriction on network access is also likely to affect your app, especially if the app relies on real-time messages such as tickles or notifications. If your app requires a persistent connection to the network to receive messages, you should use Google Cloud Messaging (GCM) if possible.

                                                                                                                                                                                                                                                                                  To confirm that your app behaves as expected with Doze, you can use adb commands to force the system to enter and exit Doze and observe your app’s behavior. For details, see Testing with Doze and App Standby.

                                                                                                                                                                                                                                                                                  Understanding App Standby

                                                                                                                                                                                                                                                                                  App Standby allows the system to determine that an app is idle when the user is not actively using it. The system makes this determination when the user does not touch the app for a certain period of time and none of the following conditions applies:

                                                                                                                                                                                                                                                                                  • The user explicitly launches the app.
                                                                                                                                                                                                                                                                                  • The app has a process currently in the foreground (either as an activity or foreground service, or in use by another activity or foreground service).
                                                                                                                                                                                                                                                                                  • The app generates a notification that users see on the lock screen or in the notification tray.

                                                                                                                                                                                                                                                                                  When the user plugs the device into a power supply, the system releases apps from the standby state, allowing them to freely access the network and to execute any pending jobs and syncs. If the device is idle for long periods of time, the system allows idle apps network access around once a day.

                                                                                                                                                                                                                                                                                  Using GCM to Interact with Your App While the Device is Idle

                                                                                                                                                                                                                                                                                  Google Cloud Messaging (GCM) is a cloud-to-device service that lets you support real-time downstream messaging between backend services and apps on Android devices. GCM provides a single, persistent connection to the cloud; all apps needing real-time messaging can share this connection. This shared connection significantly optimizes battery consumption by making it unnecessary for multiple apps to maintain their own, separate persistent connections, which can deplete the battery rapidly. For this reason, if your app requires messaging integration with a backend service, we strongly recommend that you use GCM if possible, rather than maintaining your own persistent network connection.

                                                                                                                                                                                                                                                                                  GCM is optimized to work with Doze and App Standby idle modes by means of high-priority GCM messages. GCM high-priority messages let you reliably wake your app to access the network, even if the user’s device is in Doze or the app is in App Standby mode. In Doze or App Standby mode, the system delivers the message and gives the app temporary access to network services and partial wakelocks, then returns the device or app to idle state.

                                                                                                                                                                                                                                                                                  High-priority GCM messages do not otherwise affect Doze mode, and they don’t affect the state of any other app. This means that your app can use them to communicate efficiently while minimizing battery impacts across the system and device.

                                                                                                                                                                                                                                                                                  As a general best practice, if your app requires downstream messaging, it should use GCM. If your server and client already uses GCM, make sure that your service uses high-priority messages for critical messages, since this will reliably wake apps even when the device is in Doze.

                                                                                                                                                                                                                                                                                  Support for Other Use Cases

                                                                                                                                                                                                                                                                                  Almost all apps should be able to support Doze by managing network connectivity, alarms, jobs, and syncs properly, and using GCM high-priority messages. For a narrow set of use cases, this might not be sufficient. For such cases, the system provides a configurable whitelist of apps that are partially exempt from Doze and App Standby optimizations.

                                                                                                                                                                                                                                                                                  An app that is whitelisted can use the network and hold partial wake locks during Doze and App Standby. However, other restrictions still apply to the whitelisted app, just as they do to other apps. For example, the whitelisted app’s jobs and syncs are deferred, and its regular AlarmManager alarms do not fire. An app can check whether it is currently on the exemption whitelist by calling isIgnoringBatteryOptimizations().

                                                                                                                                                                                                                                                                                  Users can manually configure the whitelist in Settings > Battery > Battery Optimization. Alternatively, the system provides ways for apps to ask users to whitelist them.

                                                                                                                                                                                                                                                                                  Before asking the user to add your app to the whitelist, make sure the app matches the acceptable use cases for whitelisting.

                                                                                                                                                                                                                                                                                  Note: Google Play policies prohibit apps from requesting direct exemption from Power Management features in Android 6.0+ (Doze and App Standby) unless the core function of the app is adversely affected.

                                                                                                                                                                                                                                                                                  Testing with Doze and App Standby

                                                                                                                                                                                                                                                                                  To ensure a great experience for your users, you should test your app fully in Doze and App Standby.

                                                                                                                                                                                                                                                                                  Testing your app with Doze

                                                                                                                                                                                                                                                                                  You can test Doze mode by following these steps:

                                                                                                                                                                                                                                                                                  1. Configure a hardware device or virtual device with an Android 6.0 (API level 23) or higher system image.
                                                                                                                                                                                                                                                                                  2. Connect the device to your development machine and install your app.
                                                                                                                                                                                                                                                                                  3. Run your app and leave it active.
                                                                                                                                                                                                                                                                                  4. Shut off the device screen. (The app remains active.)
                                                                                                                                                                                                                                                                                  5. Force the system to cycle through Doze modes by running the following commands:
                                                                                                                                                                                                                                                                                    $ adb shell dumpsys battery unplug
                                                                                                                                                                                                                                                                                    $ adb shell dumpsys deviceidle step

                                                                                                                                                                                                                                                                                    You may need to run the second command more than once. Repeat it until the device state changes to idle.

                                                                                                                                                                                                                                                                                  6. Observe the behavior of your app after you reactivate the device. Make sure the app recovers gracefully when the device exits Doze.

                                                                                                                                                                                                                                                                                  Testing your app with App Standby

                                                                                                                                                                                                                                                                                  To test the App Standby mode with your app:

                                                                                                                                                                                                                                                                                  1. Configure a hardware device or virtual device with an Android 6.0 (API level 23) or higher system image.
                                                                                                                                                                                                                                                                                  2. Connect the device to your development machine and install your app.
                                                                                                                                                                                                                                                                                  3. Run your app and leave it active.
                                                                                                                                                                                                                                                                                  4. Force the app into App Standby mode by running the following commands:
                                                                                                                                                                                                                                                                                    $ adb shell dumpsys battery unplug
                                                                                                                                                                                                                                                                                    $ adb shell am set-inactive <packageName> true
                                                                                                                                                                                                                                                                                  5. Simulate waking your app using the following commands:
                                                                                                                                                                                                                                                                                    $ adb shell am set-inactive <packageName> false
                                                                                                                                                                                                                                                                                    $ adb shell am get-inactive <packageName>
                                                                                                                                                                                                                                                                                  6. Observe the behavior of your app after waking it. Make sure the app recovers gracefully from standby mode. In particular, you should check if your app's Notifications and background jobs continue to function as expected.

                                                                                                                                                                                                                                                                                  Acceptable Use Cases for Whitelisting

                                                                                                                                                                                                                                                                                  The table below highlights the acceptable use cases for requesting or being on the Battery Optimizations exceptions whitelist. In general, your app should not be on the whitelist unless Doze or App Standby break the core function of the app or there is a technical reason why your app cannot use GCM high-priority messages.

                                                                                                                                                                                                                                                                                  For more information, see Support for Other Use Cases .

                                                                                                                                                                                                                                                                                  Type Use-case Can use GCM? Whitelisting acceptable? Notes
                                                                                                                                                                                                                                                                                  Instant messaging, chat, or calling app. Requires delivery of real-time messages to users while device is in Doze or app is in App Standby. Yes, using GCM Not Acceptable Should use GCM high-priority messages to wake the app and access the network.
                                                                                                                                                                                                                                                                                  Yes, but is not using GCM high-priority messages.
                                                                                                                                                                                                                                                                                  Instant messaging, chat, or calling app; enterprise VOIP apps. No, can not use GCM because of technical dependency on another messaging service or Doze and App Standby break the core function of the app. Acceptable
                                                                                                                                                                                                                                                                                  Task automation app App's core function is scheduling automated actions, such as for instant messaging, voice calling, new photo management, or location actions. If applicable. Acceptable
                                                                                                                                                                                                                                                                                  Peripheral device companion app App's core function is maintaining a persistent connection with the peripheral device for the purpose of providing the peripheral device internet access. If applicable. Acceptable
                                                                                                                                                                                                                                                                                  App only needs to connect to a peripheral device periodically to sync, or only needs to connect to devices, such as wireless headphones, connected via standard Bluetooth profiles. If applicable. Not Acceptable
                                                                                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                  Monitoring the Battery Level and Charging State | Android Developers Skip to content

                                                                                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                                                                                    Monitoring the Battery Level and Charging State

                                                                                                                                                                                                                                                                                    When you're altering the frequency of your background updates to reduce the effect of those updates on battery life, checking the current battery level and charging state is a good place to start.

                                                                                                                                                                                                                                                                                    The battery-life impact of performing application updates depends on the battery level and charging state of the device. The impact of performing updates while the device is charging over AC is negligible, so in most cases you can maximize your refresh rate whenever the device is connected to a wall charger. Conversely, if the device is discharging, reducing your update rate helps prolong the battery life.

                                                                                                                                                                                                                                                                                    Similarly, you can check the battery charge level, potentially reducing the frequency of—or even stopping—your updates when the battery charge is nearly exhausted.

                                                                                                                                                                                                                                                                                    Determine the Current Charging State

                                                                                                                                                                                                                                                                                    Start by determining the current charge status. The BatteryManager broadcasts all battery and charging details in a sticky Intent that includes the charging status.

                                                                                                                                                                                                                                                                                    Because it's a sticky intent, you don't need to register a BroadcastReceiver—by simply calling registerReceiver passing in null as the receiver as shown in the next snippet, the current battery status intent is returned. You could pass in an actual BroadcastReceiver object here, but we'll be handling updates in a later section so it's not necessary.

                                                                                                                                                                                                                                                                                    IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
                                                                                                                                                                                                                                                                                    Intent batteryStatus = context.registerReceiver(null, ifilter);

                                                                                                                                                                                                                                                                                    You can extract both the current charging status and, if the device is being charged, whether it's charging via USB or AC charger:

                                                                                                                                                                                                                                                                                    // Are we charging / charged?
                                                                                                                                                                                                                                                                                    int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
                                                                                                                                                                                                                                                                                    boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                                                                                                                                                                                                                                                                                                         status == BatteryManager.BATTERY_STATUS_FULL;
                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                    // How are we charging?
                                                                                                                                                                                                                                                                                    int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
                                                                                                                                                                                                                                                                                    boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
                                                                                                                                                                                                                                                                                    boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;

                                                                                                                                                                                                                                                                                    Typically you should maximize the rate of your background updates in the case where the device is connected to an AC charger, reduce the rate if the charge is over USB, and lower it further if the battery is discharging.

                                                                                                                                                                                                                                                                                    Monitor Changes in Charging State

                                                                                                                                                                                                                                                                                    The charging status can change as easily as a device can be plugged in, so it's important to monitor the charging state for changes and alter your refresh rate accordingly.

                                                                                                                                                                                                                                                                                    The BatteryManager broadcasts an action whenever the device is connected or disconnected from power. It's important to receive these events even while your app isn't running—particularly as these events should impact how often you start your app in order to initiate a background update—so you should register a BroadcastReceiver in your manifest to listen for both events by defining the ACTION_POWER_CONNECTED and ACTION_POWER_DISCONNECTED within an intent filter.

                                                                                                                                                                                                                                                                                    <receiver android:name=".PowerConnectionReceiver">
                                                                                                                                                                                                                                                                                      <intent-filter>
                                                                                                                                                                                                                                                                                        <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
                                                                                                                                                                                                                                                                                        <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
                                                                                                                                                                                                                                                                                      </intent-filter>
                                                                                                                                                                                                                                                                                    </receiver>

                                                                                                                                                                                                                                                                                    Within the associated BroadcastReceiver implementation, you can extract the current charging state and method as described in the previous step.

                                                                                                                                                                                                                                                                                    public class PowerConnectionReceiver extends BroadcastReceiver {
                                                                                                                                                                                                                                                                                        @Override
                                                                                                                                                                                                                                                                                        public void onReceive(Context context, Intent intent) {
                                                                                                                                                                                                                                                                                            int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
                                                                                                                                                                                                                                                                                            boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                                                                                                                                                                                                                                                                                                                status == BatteryManager.BATTERY_STATUS_FULL;
                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                            int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
                                                                                                                                                                                                                                                                                            boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
                                                                                                                                                                                                                                                                                            boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                    }

                                                                                                                                                                                                                                                                                    Determine the Current Battery Level

                                                                                                                                                                                                                                                                                    In some cases it's also useful to determine the current battery level. You may choose to reduce the rate of your background updates if the battery charge is below a certain level.

                                                                                                                                                                                                                                                                                    You can find the current battery charge by extracting the current battery level and scale from the battery status intent as shown here:

                                                                                                                                                                                                                                                                                    int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
                                                                                                                                                                                                                                                                                    int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                    float batteryPct = level / (float)scale;

                                                                                                                                                                                                                                                                                    Monitor Significant Changes in Battery Level

                                                                                                                                                                                                                                                                                    You can't easily continually monitor the battery state, but you don't need to.

                                                                                                                                                                                                                                                                                    Generally speaking, the impact of constantly monitoring the battery level has a greater impact on the battery than your app's normal behavior, so it's good practice to only monitor significant changes in battery level—specifically when the device enters or exits a low battery state.

                                                                                                                                                                                                                                                                                    The manifest snippet below is extracted from the intent filter element within a broadcast receiver. The receiver is triggered whenever the device battery becomes low or exits the low condition by listening for ACTION_BATTERY_LOW and ACTION_BATTERY_OKAY.

                                                                                                                                                                                                                                                                                    <receiver android:name=".BatteryLevelReceiver">
                                                                                                                                                                                                                                                                                    <intent-filter>
                                                                                                                                                                                                                                                                                      <action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
                                                                                                                                                                                                                                                                                      <action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
                                                                                                                                                                                                                                                                                      </intent-filter>
                                                                                                                                                                                                                                                                                    </receiver>

                                                                                                                                                                                                                                                                                    It is generally good practice to disable all your background updates when the battery is critically low. It doesn't matter how fresh your data is if the phone turns itself off before you can make use of it.

                                                                                                                                                                                                                                                                                    In many cases, the act of charging a device is coincident with putting it into a dock. The next lesson shows you how to determine the current dock state and monitor for changes in device docking.

                                                                                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                    Determining and Monitoring the Docking State and Type | Android Developers Skip to content

                                                                                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                                                                                      Determining and Monitoring the Docking State and Type

                                                                                                                                                                                                                                                                                      Android devices can be docked into several different kinds of docks. These include car or home docks and digital versus analog docks. The dock-state is typically closely linked to the charging state as many docks provide power to docked devices.

                                                                                                                                                                                                                                                                                      How the dock-state of the phone affects your update rate depends on your app. You may choose to increase the update frequency of a sports center app when it's in the desktop dock, or disable your updates completely if the device is car docked. Conversely, you may choose to maximize your updates while car docked if your background service is updating traffic conditions.

                                                                                                                                                                                                                                                                                      The dock state is also broadcast as a sticky Intent, allowing you to query if the device is docked or not, and if so, in which kind of dock.

                                                                                                                                                                                                                                                                                      Determine the Current Docking State

                                                                                                                                                                                                                                                                                      The dock-state details are included as an extra in a sticky broadcast of the ACTION_DOCK_EVENT action. Because it's sticky, you don't need to register a BroadcastReceiver. You can simply call registerReceiver() passing in null as the broadcast receiver as shown in the next snippet.

                                                                                                                                                                                                                                                                                      IntentFilter ifilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
                                                                                                                                                                                                                                                                                      Intent dockStatus = context.registerReceiver(null, ifilter);

                                                                                                                                                                                                                                                                                      You can extract the current docking status from the EXTRA_DOCK_STATE extra:

                                                                                                                                                                                                                                                                                      int dockState = battery.getIntExtra(EXTRA_DOCK_STATE, -1);
                                                                                                                                                                                                                                                                                      boolean isDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;

                                                                                                                                                                                                                                                                                      Determine the Current Dock Type

                                                                                                                                                                                                                                                                                      If a device is docked, it can be docked in any one of four different type of dock:

                                                                                                                                                                                                                                                                                      • Car
                                                                                                                                                                                                                                                                                      • Desk
                                                                                                                                                                                                                                                                                      • Low-End (Analog) Desk
                                                                                                                                                                                                                                                                                      • High-End (Digital) Desk

                                                                                                                                                                                                                                                                                      Note that the latter two options were only introduced to Android in API level 11, so it's good practice to check for all three where you are only interested in the type of dock rather than it being digital or analog specifically:

                                                                                                                                                                                                                                                                                      boolean isCar = dockState == EXTRA_DOCK_STATE_CAR;
                                                                                                                                                                                                                                                                                      boolean isDesk = dockState == EXTRA_DOCK_STATE_DESK ||
                                                                                                                                                                                                                                                                                                       dockState == EXTRA_DOCK_STATE_LE_DESK ||
                                                                                                                                                                                                                                                                                                       dockState == EXTRA_DOCK_STATE_HE_DESK;

                                                                                                                                                                                                                                                                                      Monitor for Changes in the Dock State or Type

                                                                                                                                                                                                                                                                                      Whenever the device is docked or undocked, the ACTION_DOCK_EVENT action is broadcast. To monitor changes in the device's dock-state, simply register a broadcast receiver in your application manifest as shown in the snippet below:

                                                                                                                                                                                                                                                                                      <action android:name="android.intent.action.ACTION_DOCK_EVENT"/>

                                                                                                                                                                                                                                                                                      You can extract the dock type and state within the receiver implementation using the same techniques described in the previous step.

                                                                                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                      Determining and Monitoring the Connectivity Status | Android Developers Skip to content

                                                                                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                                                                                        Determining and Monitoring the Connectivity Status

                                                                                                                                                                                                                                                                                        Some of the most common uses for repeating alarms and background services is to schedule regular updates of application data from Internet resources, cache data, or execute long running downloads. But if you aren't connected to the Internet, or the connection is too slow to complete your download, why both waking the device to schedule the update at all?

                                                                                                                                                                                                                                                                                        You can use the ConnectivityManager to check that you're actually connected to the Internet, and if so, what type of connection is in place.

                                                                                                                                                                                                                                                                                        Determine if You Have an Internet Connection

                                                                                                                                                                                                                                                                                        There's no need to schedule an update based on an Internet resource if you aren't connected to the Internet. The following snippet shows how to use the ConnectivityManager to query the active network and determine if it has Internet connectivity.

                                                                                                                                                                                                                                                                                        ConnectivityManager cm =
                                                                                                                                                                                                                                                                                                (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
                                                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
                                                                                                                                                                                                                                                                                        boolean isConnected = activeNetwork != null &&
                                                                                                                                                                                                                                                                                                              activeNetwork.isConnectedOrConnecting();

                                                                                                                                                                                                                                                                                        Determine the Type of your Internet Connection

                                                                                                                                                                                                                                                                                        It's also possible to determine the type of Internet connection currently available.

                                                                                                                                                                                                                                                                                        Device connectivity can be provided by mobile data, WiMAX, Wi-Fi, and ethernet connections. By querying the type of the active network, as shown below, you can alter your refresh rate based on the bandwidth available.

                                                                                                                                                                                                                                                                                        boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;

                                                                                                                                                                                                                                                                                        Mobile data costs tend to be significantly higher than Wi-Fi, so in most cases, your app's update rate should be lower when on mobile connections. Similarly, downloads of significant size should be suspended until you have a Wi-Fi connection.

                                                                                                                                                                                                                                                                                        Having disabled your updates, it's important that you listen for changes in connectivity in order to resume them once an Internet connection has been established.

                                                                                                                                                                                                                                                                                        Monitor for Changes in Connectivity

                                                                                                                                                                                                                                                                                        The ConnectivityManager broadcasts the CONNECTIVITY_ACTION ("android.net.conn.CONNECTIVITY_CHANGE") action whenever the connectivity details have changed. You can register a broadcast receiver in your manifest to listen for these changes and resume (or suspend) your background updates accordingly.

                                                                                                                                                                                                                                                                                        <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>

                                                                                                                                                                                                                                                                                        Changes to a device's connectivity can be very frequent—this broadcast is triggered every time you move between mobile data and Wi-Fi. As a result, it's good practice to monitor this broadcast only when you've previously suspended updates or downloads in order to resume them. It's generally sufficient to simply check for Internet connectivity before beginning an update and, should there be none, suspend further updates until connectivity is restored.

                                                                                                                                                                                                                                                                                        This technique requires toggling broadcast receivers you've declared in the manifest, which is described in the next lesson.

                                                                                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                        Manipulating Broadcast Receivers On Demand | Android Developers Skip to content

                                                                                                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                                                                                                        navigation

                                                                                                                                                                                                                                                                                          Manipulating Broadcast Receivers On Demand

                                                                                                                                                                                                                                                                                          The simplest way to monitor device state changes is to create a BroadcastReceiver for each state you're monitoring and register each of them in your application manifest. Then within each of these receivers you simply reschedule your recurring alarms based on the current device state.

                                                                                                                                                                                                                                                                                          A side-effect of this approach is that your app will wake the device each time any of these receivers is triggered—potentially much more frequently than required.

                                                                                                                                                                                                                                                                                          A better approach is to disable or enable the broadcast receivers at runtime. That way you can use the receivers you declared in the manifest as passive alarms that are triggered by system events only when necessary.

                                                                                                                                                                                                                                                                                          Toggle and Cascade State Change Receivers to Improve Efficiency

                                                                                                                                                                                                                                                                                          You can use the PackageManager to toggle the enabled state on any component defined in the manifest, including whichever broadcast receivers you wish to enable or disable as shown in the snippet below:

                                                                                                                                                                                                                                                                                          ComponentName receiver = new ComponentName(context, myReceiver.class);
                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                          PackageManager pm = context.getPackageManager();
                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                          pm.setComponentEnabledSetting(receiver,
                                                                                                                                                                                                                                                                                                  PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                                                                                                                                                                                                                                                                                                  PackageManager.DONT_KILL_APP)

                                                                                                                                                                                                                                                                                          Using this technique, if you determine that connectivity has been lost, you can disable all of your receivers except the connectivity-change receiver. Conversely, once you are connected you can stop listening for connectivity changes and simply check to see if you're online immediately before performing an update and rescheduling a recurring update alarm.

                                                                                                                                                                                                                                                                                          You can use the same technique to delay a download that requires higher bandwidth to complete. Simply enable a broadcast receiver that listens for connectivity changes and initiates the download only after you are connected to Wi-Fi.

                                                                                                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                          Reducing Network Battery Drain | Android Developers Skip to content

                                                                                                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                                                                                                          navigation

                                                                                                                                                                                                                                                                                            Reducing Network Battery Drain

                                                                                                                                                                                                                                                                                            Requests that your app makes to the network are a major cause of battery drain because they turn on the power-hungry mobile or Wi-Fi radios. Beyond the power needed to send and receive packets, these radios expend extra power just turning on and keeping awake. Something as simple as a network request every 15 seconds can keep the mobile radio on continuously and quickly use up battery power.

                                                                                                                                                                                                                                                                                            This lesson shows you how to tag your app's source code to categorize, visualize and color your network requests according to how they are initiated. From there, each category identifies areas of your app that you can make more battery-efficient.

                                                                                                                                                                                                                                                                                            Performance Actions

                                                                                                                                                                                                                                                                                            Collecting Network Traffic Data
                                                                                                                                                                                                                                                                                            Learn how to instrument your app's code and gather data on its use of network resources.
                                                                                                                                                                                                                                                                                            Analyzing Network Traffic Data
                                                                                                                                                                                                                                                                                            Learn how to analyze your app's use of network resources in response to user actions and optimize it to reduce power consumption.
                                                                                                                                                                                                                                                                                            Optimizing User-Initiated Network Use
                                                                                                                                                                                                                                                                                            Learn how to optimize your app's use of network resources in response to user actions to reduce power consumption.
                                                                                                                                                                                                                                                                                            Optimizing App-Initiated Network Use
                                                                                                                                                                                                                                                                                            Learn how to optimize your app's requests for network resources to reduce power consumption.
                                                                                                                                                                                                                                                                                            Optimizing Server-Initiated Network Use
                                                                                                                                                                                                                                                                                            Learn how to optimize your app's requests for network resources and to reduce power consumption.
                                                                                                                                                                                                                                                                                            Optimizing General Network Use
                                                                                                                                                                                                                                                                                            Learn how to optimize your app's requests for network resources and to reduce power consumption.
                                                                                                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                            Sending Operations to Multiple Threads | Android Developers Skip to content

                                                                                                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                                                                                                            navigation

                                                                                                                                                                                                                                                                                              Sending Operations to Multiple Threads

                                                                                                                                                                                                                                                                                              Dependencies and prerequisites

                                                                                                                                                                                                                                                                                              You should also read

                                                                                                                                                                                                                                                                                              Try it out

                                                                                                                                                                                                                                                                                              Download the sample

                                                                                                                                                                                                                                                                                              ThreadSample.zip

                                                                                                                                                                                                                                                                                              The speed and efficiency of a long-running, data-intensive operation often improves when you split it into smaller operations running on multiple threads. On a device that has a CPU with multiple processors (cores), the system can run the threads in parallel, rather than making each sub-operation wait for a chance to run. For example, decoding multiple image files in order to display them on a thumbnail screen runs substantially faster when you do each decode on a separate thread.

                                                                                                                                                                                                                                                                                              This class shows you how to set up and use multiple threads in an Android app, using a thread pool object. You'll also learn how to define code to run on a thread and how to communicate between one of these threads and the UI thread.

                                                                                                                                                                                                                                                                                              Lessons

                                                                                                                                                                                                                                                                                              Specifying the Code to Run on a Thread
                                                                                                                                                                                                                                                                                              Learn how to write code to run on a separate Thread, by defining a class that implements the Runnable interface.
                                                                                                                                                                                                                                                                                              Creating a Manager for Multiple Threads
                                                                                                                                                                                                                                                                                              Learn how to create an object that manages a pool of Thread objects and a queue of Runnable objects. This object is called a ThreadPoolExecutor.
                                                                                                                                                                                                                                                                                              Running Code on a Thread Pool Thread
                                                                                                                                                                                                                                                                                              Learn how to run a Runnable on a thread from the thread pool.
                                                                                                                                                                                                                                                                                              Communicating with the UI Thread
                                                                                                                                                                                                                                                                                              Learn how to communicate from a thread in the thread pool to the UI thread.
                                                                                                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                              Specifying the Code to Run on a Thread | Android Developers Skip to content

                                                                                                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                                                                                                Specifying the Code to Run on a Thread

                                                                                                                                                                                                                                                                                                This lesson teaches you to

                                                                                                                                                                                                                                                                                                1. Define a Class that Implements Runnable
                                                                                                                                                                                                                                                                                                2. Implement the run() Method

                                                                                                                                                                                                                                                                                                You should also read

                                                                                                                                                                                                                                                                                                Try it out

                                                                                                                                                                                                                                                                                                Download the sample

                                                                                                                                                                                                                                                                                                ThreadSample.zip

                                                                                                                                                                                                                                                                                                This lesson shows you how to implement a Runnable class, which runs the code in its Runnable.run() method on a separate thread. You can also pass a Runnable to another object that can then attach it to a thread and run it. One or more Runnable objects that perform a particular operation are sometimes called a task.

                                                                                                                                                                                                                                                                                                Thread and Runnable are basic classes that, on their own, have only limited power. Instead, they're the basis of powerful Android classes such as HandlerThread, AsyncTask, and IntentService. Thread and Runnable are also the basis of the class ThreadPoolExecutor. This class automatically manages threads and task queues, and can even run multiple threads in parallel.

                                                                                                                                                                                                                                                                                                Define a Class that Implements Runnable

                                                                                                                                                                                                                                                                                                Implementing a class that implements Runnable is straightforward. For example:

                                                                                                                                                                                                                                                                                                public class PhotoDecodeRunnable implements Runnable {
                                                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                                                                    public void run() {
                                                                                                                                                                                                                                                                                                        /*
                                                                                                                                                                                                                                                                                                         * Code you want to run on the thread goes here
                                                                                                                                                                                                                                                                                                         */
                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                

                                                                                                                                                                                                                                                                                                Implement the run() Method

                                                                                                                                                                                                                                                                                                In the class, the Runnable.run() method contains the code that's executed. Usually, anything is allowable in a Runnable. Remember, though, that the Runnable won't be running on the UI thread, so it can't directly modify UI objects such as View objects. To communicate with the UI thread, you have to use the techniques described in the lesson Communicate with the UI Thread.

                                                                                                                                                                                                                                                                                                At the beginning of the run() method, set the thread to use background priority by calling Process.setThreadPriority() with THREAD_PRIORITY_BACKGROUND. This approach reduces resource competition between the Runnable object's thread and the UI thread.

                                                                                                                                                                                                                                                                                                You should also store a reference to the Runnable object's Thread in the Runnable itself, by calling Thread.currentThread().

                                                                                                                                                                                                                                                                                                The following snippet shows how to set up the run() method:

                                                                                                                                                                                                                                                                                                class PhotoDecodeRunnable implements Runnable {
                                                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                                                    /*
                                                                                                                                                                                                                                                                                                     * Defines the code to run for this task.
                                                                                                                                                                                                                                                                                                     */
                                                                                                                                                                                                                                                                                                    @Override
                                                                                                                                                                                                                                                                                                    public void run() {
                                                                                                                                                                                                                                                                                                        // Moves the current Thread into the background
                                                                                                                                                                                                                                                                                                        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                        /*
                                                                                                                                                                                                                                                                                                         * Stores the current Thread in the PhotoTask instance,
                                                                                                                                                                                                                                                                                                         * so that the instance
                                                                                                                                                                                                                                                                                                         * can interrupt the Thread.
                                                                                                                                                                                                                                                                                                         */
                                                                                                                                                                                                                                                                                                        mPhotoTask.setImageDecodeThread(Thread.currentThread());
                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                Creating a Manager for Multiple Threads | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                                                                                                  Creating a Manager for Multiple Threads

                                                                                                                                                                                                                                                                                                  The previous lesson showed how to define a task that executes on a separate thread. If you only want to run the task once, this may be all you need. If you want to run a task repeatedly on different sets of data, but you only need one execution running at a time, an IntentService suits your needs. To automatically run tasks as resources become available, or to allow multiple tasks to run at the same time (or both), you need to provide a managed collection of threads. To do this, use an instance of ThreadPoolExecutor, which runs a task from a queue when a thread in its pool becomes free. To run a task, all you have to do is add it to the queue.

                                                                                                                                                                                                                                                                                                  A thread pool can run multiple parallel instances of a task, so you should ensure that your code is thread-safe. Enclose variables that can be accessed by more than one thread in a synchronized block. This approach will prevent one thread from reading the variable while another is writing to it. Typically, this situation arises with static variables, but it also occurs in any object that is only instantiated once. To learn more about this, read the Processes and Threads API guide.

                                                                                                                                                                                                                                                                                                  Define the Thread Pool Class

                                                                                                                                                                                                                                                                                                  Instantiate ThreadPoolExecutor in its own class. Within this class, do the following:

                                                                                                                                                                                                                                                                                                  Use static variables for thread pools
                                                                                                                                                                                                                                                                                                  You may only want a single instance of a thread pool for your app, in order to have a single control point for restricted CPU or network resources. If you have different Runnable types, you may want to have a thread pool for each one, but each of these can be a single instance. For example, you can add this as part of your global field declarations:
                                                                                                                                                                                                                                                                                                  public class PhotoManager {
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                      static  {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Creates a single static instance of PhotoManager
                                                                                                                                                                                                                                                                                                          sInstance = new PhotoManager();
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                  Use a private constructor
                                                                                                                                                                                                                                                                                                  Making the constructor private ensures that it is a singleton, which means that you don't have to enclose accesses to the class in a synchronized block:
                                                                                                                                                                                                                                                                                                  public class PhotoManager {
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                      /**
                                                                                                                                                                                                                                                                                                       * Constructs the work queues and thread pools used to download
                                                                                                                                                                                                                                                                                                       * and decode images. Because the constructor is marked private,
                                                                                                                                                                                                                                                                                                       * it's unavailable to other classes, even in the same package.
                                                                                                                                                                                                                                                                                                       */
                                                                                                                                                                                                                                                                                                      private PhotoManager() {
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                  Start your tasks by calling methods in the thread pool class.
                                                                                                                                                                                                                                                                                                  Define a method in the thread pool class that adds a task to a thread pool's queue. For example:
                                                                                                                                                                                                                                                                                                  public class PhotoManager {
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                      // Called by the PhotoView to get a photo
                                                                                                                                                                                                                                                                                                      static public PhotoTask startDownload(
                                                                                                                                                                                                                                                                                                          PhotoView imageView,
                                                                                                                                                                                                                                                                                                          boolean cacheFlag) {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Adds a download task to the thread pool for execution
                                                                                                                                                                                                                                                                                                          sInstance.
                                                                                                                                                                                                                                                                                                                  mDownloadThreadPool.
                                                                                                                                                                                                                                                                                                                  execute(downloadTask.getHTTPDownloadRunnable());
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                  Instantiate a Handler in the constructor and attach it to your app's UI thread.
                                                                                                                                                                                                                                                                                                  A Handler allows your app to safely call the methods of UI objects such as View objects. Most UI objects may only be safely altered from the UI thread. This approach is described in more detail in the lesson Communicate with the UI Thread. For example:
                                                                                                                                                                                                                                                                                                      private PhotoManager() {
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                          // Defines a Handler object that's attached to the UI thread
                                                                                                                                                                                                                                                                                                          mHandler = new Handler(Looper.getMainLooper()) {
                                                                                                                                                                                                                                                                                                              /*
                                                                                                                                                                                                                                                                                                               * handleMessage() defines the operations to perform when
                                                                                                                                                                                                                                                                                                               * the Handler receives a new Message to process.
                                                                                                                                                                                                                                                                                                               */
                                                                                                                                                                                                                                                                                                              @Override
                                                                                                                                                                                                                                                                                                              public void handleMessage(Message inputMessage) {
                                                                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                  Determine the Thread Pool Parameters

                                                                                                                                                                                                                                                                                                  Once you have the overall class structure, you can start defining the thread pool. To instantiate a ThreadPoolExecutor object, you need the following values:

                                                                                                                                                                                                                                                                                                  Initial pool size and maximum pool size
                                                                                                                                                                                                                                                                                                  The initial number of threads to allocate to the pool, and the maximum allowable number. The number of threads you can have in a thread pool depends primarily on the number of cores available for your device. This number is available from the system environment:
                                                                                                                                                                                                                                                                                                  public class PhotoManager {
                                                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                                                                      /*
                                                                                                                                                                                                                                                                                                       * Gets the number of available cores
                                                                                                                                                                                                                                                                                                       * (not always the same as the maximum number of cores)
                                                                                                                                                                                                                                                                                                       */
                                                                                                                                                                                                                                                                                                      private static int NUMBER_OF_CORES =
                                                                                                                                                                                                                                                                                                              Runtime.getRuntime().availableProcessors();
                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                  This number may not reflect the number of physical cores in the device; some devices have CPUs that deactivate one or more cores depending on the system load. For these devices, availableProcessors() returns the number of active cores, which may be less than the total number of cores.
                                                                                                                                                                                                                                                                                                  Keep alive time and time unit
                                                                                                                                                                                                                                                                                                  The duration that a thread will remain idle before it shuts down. The duration is interpreted by the time unit value, one of the constants defined in TimeUnit.
                                                                                                                                                                                                                                                                                                  A queue of tasks
                                                                                                                                                                                                                                                                                                  The incoming queue from which ThreadPoolExecutor takes Runnable objects. To start code on a thread, a thread pool manager takes a Runnable object from a first-in, first-out queue and attaches it to the thread. You provide this queue object when you create the thread pool, using any queue class that implements the BlockingQueue interface. To match the requirements of your app, you can choose from the available queue implementations; to learn more about them, see the class overview for ThreadPoolExecutor. This example uses the LinkedBlockingQueue class:
                                                                                                                                                                                                                                                                                                  public class PhotoManager {
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                      private PhotoManager() {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // A queue of Runnables
                                                                                                                                                                                                                                                                                                          private final BlockingQueue<Runnable> mDecodeWorkQueue;
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Instantiates the queue of Runnables as a LinkedBlockingQueue
                                                                                                                                                                                                                                                                                                          mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                  Create a Pool of Threads

                                                                                                                                                                                                                                                                                                  To create a pool of threads, instantiate a thread pool manager by calling ThreadPoolExecutor(). This creates and manages a constrained group of threads. Because the initial pool size and the maximum pool size are the same, ThreadPoolExecutor creates all of the thread objects when it is instantiated. For example:

                                                                                                                                                                                                                                                                                                      private PhotoManager() {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Sets the amount of time an idle thread waits before terminating
                                                                                                                                                                                                                                                                                                          private static final int KEEP_ALIVE_TIME = 1;
                                                                                                                                                                                                                                                                                                          // Sets the Time Unit to seconds
                                                                                                                                                                                                                                                                                                          private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
                                                                                                                                                                                                                                                                                                          // Creates a thread pool manager
                                                                                                                                                                                                                                                                                                          mDecodeThreadPool = new ThreadPoolExecutor(
                                                                                                                                                                                                                                                                                                                  NUMBER_OF_CORES,       // Initial pool size
                                                                                                                                                                                                                                                                                                                  NUMBER_OF_CORES,       // Max pool size
                                                                                                                                                                                                                                                                                                                  KEEP_ALIVE_TIME,
                                                                                                                                                                                                                                                                                                                  KEEP_ALIVE_TIME_UNIT,
                                                                                                                                                                                                                                                                                                                  mDecodeWorkQueue);
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                  Running Code on a Thread Pool Thread | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                                                                                                    Running Code on a Thread Pool Thread

                                                                                                                                                                                                                                                                                                    This lesson teaches you to

                                                                                                                                                                                                                                                                                                    1. Run a Runnable on a Thread in the Thread Pool
                                                                                                                                                                                                                                                                                                    2. Interrupt Running Code

                                                                                                                                                                                                                                                                                                    You should also read

                                                                                                                                                                                                                                                                                                    Try it out

                                                                                                                                                                                                                                                                                                    Download the sample

                                                                                                                                                                                                                                                                                                    ThreadSample.zip

                                                                                                                                                                                                                                                                                                    The previous lesson showed you how to define a class that manages thread pools and the tasks that run on them. This lesson shows you how to run a task on a thread pool. To do this, you add the task to the pool's work queue. When a thread becomes available, the ThreadPoolExecutor takes a task from the queue and runs it on the thread.

                                                                                                                                                                                                                                                                                                    This lesson also shows you how to stop a task that's running. You might want to do this if a task starts, but then discovers that its work isn't necessary. Rather than wasting processor time, you can cancel the thread the task is running on. For example, if you are downloading images from the network and using a cache, you probably want to stop a task if it detects that an image is already present in the cache. Depending on how you write your app, you may not be able to detect this before you start the download.

                                                                                                                                                                                                                                                                                                    Run a Task on a Thread in the Thread Pool

                                                                                                                                                                                                                                                                                                    To start a task object on a thread in a particular thread pool, pass the Runnable to ThreadPoolExecutor.execute(). This call adds the task to the thread pool's work queue. When an idle thread becomes available, the manager takes the task that has been waiting the longest and runs it on the thread:

                                                                                                                                                                                                                                                                                                    public class PhotoManager {
                                                                                                                                                                                                                                                                                                        public void handleState(PhotoTask photoTask, int state) {
                                                                                                                                                                                                                                                                                                            switch (state) {
                                                                                                                                                                                                                                                                                                                // The task finished downloading the image
                                                                                                                                                                                                                                                                                                                case DOWNLOAD_COMPLETE:
                                                                                                                                                                                                                                                                                                                // Decodes the image
                                                                                                                                                                                                                                                                                                                    mDecodeThreadPool.execute(
                                                                                                                                                                                                                                                                                                                            photoTask.getPhotoDecodeRunnable());
                                                                                                                                                                                                                                                                                                                ...
                                                                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                    When ThreadPoolExecutor starts a Runnable on a thread, it automatically calls the object's run() method.

                                                                                                                                                                                                                                                                                                    Interrupt Running Code

                                                                                                                                                                                                                                                                                                    To stop a task, you need to interrupt the task's thread. To prepare to do this, you need to store a handle to the task's thread when you create the task. For example:

                                                                                                                                                                                                                                                                                                    class PhotoDecodeRunnable implements Runnable {
                                                                                                                                                                                                                                                                                                        // Defines the code to run for this task
                                                                                                                                                                                                                                                                                                        public void run() {
                                                                                                                                                                                                                                                                                                            /*
                                                                                                                                                                                                                                                                                                             * Stores the current Thread in the
                                                                                                                                                                                                                                                                                                             * object that contains PhotoDecodeRunnable
                                                                                                                                                                                                                                                                                                             */
                                                                                                                                                                                                                                                                                                            mPhotoTask.setImageDecodeThread(Thread.currentThread());
                                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                    To interrupt a thread, call Thread.interrupt(). Notice that Thread objects are controlled by the system, which can modify them outside of your app's process. For this reason, you need to lock access on a thread before you interrupt it, by placing the access in a synchronized block. For example:

                                                                                                                                                                                                                                                                                                    public class PhotoManager {
                                                                                                                                                                                                                                                                                                        public static void cancelAll() {
                                                                                                                                                                                                                                                                                                            /*
                                                                                                                                                                                                                                                                                                             * Creates an array of Runnables that's the same size as the
                                                                                                                                                                                                                                                                                                             * thread pool work queue
                                                                                                                                                                                                                                                                                                             */
                                                                                                                                                                                                                                                                                                            Runnable[] runnableArray = new Runnable[mDecodeWorkQueue.size()];
                                                                                                                                                                                                                                                                                                            // Populates the array with the Runnables in the queue
                                                                                                                                                                                                                                                                                                            mDecodeWorkQueue.toArray(runnableArray);
                                                                                                                                                                                                                                                                                                            // Stores the array length in order to iterate over the array
                                                                                                                                                                                                                                                                                                            int len = runnableArray.length;
                                                                                                                                                                                                                                                                                                            /*
                                                                                                                                                                                                                                                                                                             * Iterates over the array of Runnables and interrupts each one's Thread.
                                                                                                                                                                                                                                                                                                             */
                                                                                                                                                                                                                                                                                                            synchronized (sInstance) {
                                                                                                                                                                                                                                                                                                                // Iterates over the array of tasks
                                                                                                                                                                                                                                                                                                                for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) {
                                                                                                                                                                                                                                                                                                                    // Gets the current thread
                                                                                                                                                                                                                                                                                                                    Thread thread = runnableArray[taskArrayIndex].mThread;
                                                                                                                                                                                                                                                                                                                    // if the Thread exists, post an interrupt to it
                                                                                                                                                                                                                                                                                                                    if (null != thread) {
                                                                                                                                                                                                                                                                                                                        thread.interrupt();
                                                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                    In most cases, Thread.interrupt() stops the thread immediately. However, it only stops threads that are waiting, and will not interrupt CPU or network-intensive tasks. To avoid slowing down or locking up the system, you should test for any pending interrupt requests before attempting an operation :

                                                                                                                                                                                                                                                                                                    /*
                                                                                                                                                                                                                                                                                                     * Before continuing, checks to see that the Thread hasn't
                                                                                                                                                                                                                                                                                                     * been interrupted
                                                                                                                                                                                                                                                                                                     */
                                                                                                                                                                                                                                                                                                    if (Thread.interrupted()) {
                                                                                                                                                                                                                                                                                                        return;
                                                                                                                                                                                                                                                                                                    }
                                                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                                                    // Decodes a byte array into a Bitmap (CPU-intensive)
                                                                                                                                                                                                                                                                                                    BitmapFactory.decodeByteArray(
                                                                                                                                                                                                                                                                                                            imageBuffer, 0, imageBuffer.length, bitmapOptions);
                                                                                                                                                                                                                                                                                                    ...
                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                    Communicating with the UI Thread | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                                                                                                      Communicating with the UI Thread

                                                                                                                                                                                                                                                                                                      This lesson teaches you to

                                                                                                                                                                                                                                                                                                      1. Define a Handler on the UI Thread
                                                                                                                                                                                                                                                                                                      2. Move Data from a Task to the UI Thread

                                                                                                                                                                                                                                                                                                      You should also read

                                                                                                                                                                                                                                                                                                      Try it out

                                                                                                                                                                                                                                                                                                      Download the sample

                                                                                                                                                                                                                                                                                                      ThreadSample.zip

                                                                                                                                                                                                                                                                                                      In the previous lesson you learned how to start a task on a thread managed by ThreadPoolExecutor. This final lesson shows you how to send data from the task to objects running on the user interface (UI) thread. This feature allows your tasks to do background work and then move the results to UI elements such as bitmaps.

                                                                                                                                                                                                                                                                                                      Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren't running on your UI thread, they don't have access to UI objects. To move data from a background thread to the UI thread, use a Handler that's running on the UI thread.

                                                                                                                                                                                                                                                                                                      Define a Handler on the UI Thread

                                                                                                                                                                                                                                                                                                      Handler is part of the Android system's framework for managing threads. A Handler object receives messages and runs code to handle the messages. Normally, you create a Handler for a new thread, but you can also create a Handler that's connected to an existing thread. When you connect a Handler to your UI thread, the code that handles messages runs on the UI thread.

                                                                                                                                                                                                                                                                                                      Instantiate the Handler object in the constructor for the class that creates your thread pools, and store the object in a global variable. Connect it to the UI thread by instantiating it with the Handler(Looper) constructor. This constructor uses a Looper object, which is another part of the Android system's thread management framework. When you instantiate a Handler based on a particular Looper instance, the Handler runs on the same thread as the Looper. For example:

                                                                                                                                                                                                                                                                                                      private PhotoManager() {
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                          // Defines a Handler object that's attached to the UI thread
                                                                                                                                                                                                                                                                                                          mHandler = new Handler(Looper.getMainLooper()) {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                      Inside the Handler, override the handleMessage() method. The Android system invokes this method when it receives a new message for a thread it's managing; all of the Handler objects for a particular thread receive the same message. For example:

                                                                                                                                                                                                                                                                                                              /*
                                                                                                                                                                                                                                                                                                               * handleMessage() defines the operations to perform when
                                                                                                                                                                                                                                                                                                               * the Handler receives a new Message to process.
                                                                                                                                                                                                                                                                                                               */
                                                                                                                                                                                                                                                                                                              @Override
                                                                                                                                                                                                                                                                                                              public void handleMessage(Message inputMessage) {
                                                                                                                                                                                                                                                                                                                  // Gets the image task from the incoming Message object.
                                                                                                                                                                                                                                                                                                                  PhotoTask photoTask = (PhotoTask) inputMessage.obj;
                                                                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                      The next section shows how to tell the Handler to move data.
                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                      Move Data from a Task to the UI Thread

                                                                                                                                                                                                                                                                                                      To move data from a task object running on a background thread to an object on the UI thread, start by storing references to the data and the UI object in the task object. Next, pass the task object and a status code to the object that instantiated the Handler. In this object, send a Message containing the status and the task object to the Handler. Because Handler is running on the UI thread, it can move the data to the UI object.

                                                                                                                                                                                                                                                                                                      Store data in the task object

                                                                                                                                                                                                                                                                                                      For example, here's a Runnable, running on a background thread, that decodes a Bitmap and stores it in its parent object PhotoTask. The Runnable also stores the status code DECODE_STATE_COMPLETED.

                                                                                                                                                                                                                                                                                                      // A class that decodes photo files into Bitmaps
                                                                                                                                                                                                                                                                                                      class PhotoDecodeRunnable implements Runnable {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          PhotoDecodeRunnable(PhotoTask downloadTask) {
                                                                                                                                                                                                                                                                                                              mPhotoTask = downloadTask;
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Gets the downloaded byte array
                                                                                                                                                                                                                                                                                                          byte[] imageBuffer = mPhotoTask.getByteBuffer();
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Runs the code for this task
                                                                                                                                                                                                                                                                                                          public void run() {
                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                              // Tries to decode the image buffer
                                                                                                                                                                                                                                                                                                              returnBitmap = BitmapFactory.decodeByteArray(
                                                                                                                                                                                                                                                                                                                      imageBuffer,
                                                                                                                                                                                                                                                                                                                      0,
                                                                                                                                                                                                                                                                                                                      imageBuffer.length,
                                                                                                                                                                                                                                                                                                                      bitmapOptions
                                                                                                                                                                                                                                                                                                              );
                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                              // Sets the ImageView Bitmap
                                                                                                                                                                                                                                                                                                              mPhotoTask.setImage(returnBitmap);
                                                                                                                                                                                                                                                                                                              // Reports a status of "completed"
                                                                                                                                                                                                                                                                                                              mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);
                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                      PhotoTask also contains a handle to the ImageView that displays the Bitmap. Even though references to the Bitmap and ImageView are in the same object, you can't assign the Bitmap to the ImageView, because you're not currently running on the UI thread.

                                                                                                                                                                                                                                                                                                      Instead, the next step is to send this status to the PhotoTask object.

                                                                                                                                                                                                                                                                                                      Send status up the object hierarchy

                                                                                                                                                                                                                                                                                                      PhotoTask is the next higher object in the hierarchy. It maintains references to the decoded data and the View object that will show the data. It receives a status code from PhotoDecodeRunnable and passes it along to the object that maintains thread pools and instantiates Handler:

                                                                                                                                                                                                                                                                                                      public class PhotoTask {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Gets a handle to the object that creates the thread pools
                                                                                                                                                                                                                                                                                                          sPhotoManager = PhotoManager.getInstance();
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          public void handleDecodeState(int state) {
                                                                                                                                                                                                                                                                                                              int outState;
                                                                                                                                                                                                                                                                                                              // Converts the decode state to the overall state.
                                                                                                                                                                                                                                                                                                              switch(state) {
                                                                                                                                                                                                                                                                                                                  case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:
                                                                                                                                                                                                                                                                                                                      outState = PhotoManager.TASK_COMPLETE;
                                                                                                                                                                                                                                                                                                                      break;
                                                                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                              // Calls the generalized state method
                                                                                                                                                                                                                                                                                                              handleState(outState);
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Passes the state to PhotoManager
                                                                                                                                                                                                                                                                                                          void handleState(int state) {
                                                                                                                                                                                                                                                                                                              /*
                                                                                                                                                                                                                                                                                                               * Passes a handle to this task and the
                                                                                                                                                                                                                                                                                                               * current state to the class that created
                                                                                                                                                                                                                                                                                                               * the thread pools
                                                                                                                                                                                                                                                                                                               */
                                                                                                                                                                                                                                                                                                              sPhotoManager.handleState(this, state);
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                      Move data to the UI

                                                                                                                                                                                                                                                                                                      From the PhotoTask object, the PhotoManager object receives a status code and a handle to the PhotoTask object. Because the status is TASK_COMPLETE, creates a Message containing the state and task object and sends it to the Handler:

                                                                                                                                                                                                                                                                                                      public class PhotoManager {
                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                          // Handle status messages from tasks
                                                                                                                                                                                                                                                                                                          public void handleState(PhotoTask photoTask, int state) {
                                                                                                                                                                                                                                                                                                              switch (state) {
                                                                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                                                                                  // The task finished downloading and decoding the image
                                                                                                                                                                                                                                                                                                                  case TASK_COMPLETE:
                                                                                                                                                                                                                                                                                                                      /*
                                                                                                                                                                                                                                                                                                                       * Creates a message for the Handler
                                                                                                                                                                                                                                                                                                                       * with the state and the task object
                                                                                                                                                                                                                                                                                                                       */
                                                                                                                                                                                                                                                                                                                      Message completeMessage =
                                                                                                                                                                                                                                                                                                                              mHandler.obtainMessage(state, photoTask);
                                                                                                                                                                                                                                                                                                                      completeMessage.sendToTarget();
                                                                                                                                                                                                                                                                                                                      break;
                                                                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                      Finally, Handler.handleMessage() checks the status code for each incoming Message. If the status code is TASK_COMPLETE, then the task is finished, and the PhotoTask object in the Message contains both a Bitmap and an ImageView. Because Handler.handleMessage() is running on the UI thread, it can safely move the Bitmap to the ImageView:

                                                                                                                                                                                                                                                                                                          private PhotoManager() {
                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                                  mHandler = new Handler(Looper.getMainLooper()) {
                                                                                                                                                                                                                                                                                                                      @Override
                                                                                                                                                                                                                                                                                                                      public void handleMessage(Message inputMessage) {
                                                                                                                                                                                                                                                                                                                          // Gets the task from the incoming Message object.
                                                                                                                                                                                                                                                                                                                          PhotoTask photoTask = (PhotoTask) inputMessage.obj;
                                                                                                                                                                                                                                                                                                                          // Gets the ImageView for this task
                                                                                                                                                                                                                                                                                                                          PhotoView localView = photoTask.getPhotoView();
                                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                                          switch (inputMessage.what) {
                                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                                              // The decoding is done
                                                                                                                                                                                                                                                                                                                              case TASK_COMPLETE:
                                                                                                                                                                                                                                                                                                                                  /*
                                                                                                                                                                                                                                                                                                                                   * Moves the Bitmap from the task
                                                                                                                                                                                                                                                                                                                                   * to the View
                                                                                                                                                                                                                                                                                                                                   */
                                                                                                                                                                                                                                                                                                                                  localView.setImageBitmap(photoTask.getImage());
                                                                                                                                                                                                                                                                                                                                  break;
                                                                                                                                                                                                                                                                                                                              ...
                                                                                                                                                                                                                                                                                                                              default:
                                                                                                                                                                                                                                                                                                                                  /*
                                                                                                                                                                                                                                                                                                                                   * Pass along other messages from the UI
                                                                                                                                                                                                                                                                                                                                   */
                                                                                                                                                                                                                                                                                                                                  super.handleMessage(inputMessage);
                                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                  ...
                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                      Best Practices for Security & Privacy | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                                                                                                        Best Practices for Security & Privacy

                                                                                                                                                                                                                                                                                                        These classes and articles provide information about how to keep your app's data secure.

                                                                                                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                        Best Practices for Permissions and Identifiers | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                                                                                                                        navigation

                                                                                                                                                                                                                                                                                                          Best Practices for Permissions and Identifiers

                                                                                                                                                                                                                                                                                                          The articles below highlight key guidelines for using permissions and identifiers properly in your apps.

                                                                                                                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                          Best Practices for Testing | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                                                                                                                          navigation

                                                                                                                                                                                                                                                                                                            Best Practices for Testing

                                                                                                                                                                                                                                                                                                            Testing your app is an integral part of the app development process. Testing allows you to verify the correctness, functional behavior, and usability of your app before it is released publicly.



                                                                                                                                                                                                                                                                                                            Get Started

                                                                                                                                                                                                                                                                                                            Learn the basics of testing your app, with information about building and running your tests with Android Studio:

                                                                                                                                                                                                                                                                                                            Testing Tools and APIs

                                                                                                                                                                                                                                                                                                            Learn about the tools provided by the Android platform that help you test every aspect of your app at every level:

                                                                                                                                                                                                                                                                                                            App Testing Techniques

                                                                                                                                                                                                                                                                                                            Learn techniques for testing your apps:

                                                                                                                                                                                                                                                                                                            Other Resources

                                                                                                                                                                                                                                                                                                            More resources for app testing:

                                                                                                                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                            Building Effective Unit Tests | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                                                                                                                            navigation

                                                                                                                                                                                                                                                                                                              Building Effective Unit Tests

                                                                                                                                                                                                                                                                                                              You should also read

                                                                                                                                                                                                                                                                                                              Unit tests are the fundamental tests in your app testing strategy. By creating and running unit tests against your code, you can easily verify that the logic of individual units is correct. Running unit tests after every build helps you to quickly catch and fix software regressions introduced by code changes to your app.

                                                                                                                                                                                                                                                                                                              A unit test generally exercises the functionality of the smallest possible unit of code (which could be a method, class, or component) in a repeatable way. You should build unit tests when you need to verify the logic of specific code in your app. For example, if you are unit testing a class, your test might check that the class is in the right state. Typically, the unit of code is tested in isolation; your test affects and monitors changes to that unit only. A mocking framework can be used to isolate your unit from its dependencies.

                                                                                                                                                                                                                                                                                                              Note: Unit tests are not suitable for testing complex UI interaction events. Instead, you should use the UI testing frameworks, as described in Automating UI Tests.

                                                                                                                                                                                                                                                                                                              For testing Android apps, you typically create these types of automated unit tests:

                                                                                                                                                                                                                                                                                                              • Local tests: Unit tests that run on your local machine only. These tests are compiled to run locally on the Java Virtual Machine (JVM) to minimize execution time. Use this approach to run unit tests that have no dependencies on the Android framework or have dependencies that can be filled by using mock objects.
                                                                                                                                                                                                                                                                                                              • Instrumented tests: Unit tests that run on an Android device or emulator. These tests have access to instrumentation information, such as the Context for the app under test. Use this approach to run unit tests that have Android dependencies which cannot be easily filled by using mock objects.

                                                                                                                                                                                                                                                                                                              The lessons in this class teach you how to build these types of automated unit tests.

                                                                                                                                                                                                                                                                                                              Lessons

                                                                                                                                                                                                                                                                                                              Building Local Unit Tests
                                                                                                                                                                                                                                                                                                              Learn how to build unit tests that run on your local machine.
                                                                                                                                                                                                                                                                                                              Building Instrumented Unit Tests
                                                                                                                                                                                                                                                                                                              Learn how to build unit tests that run on an Android device or emulator.
                                                                                                                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                              Automating User Interface Tests | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                                                                                                                Automating User Interface Tests

                                                                                                                                                                                                                                                                                                                You should also read

                                                                                                                                                                                                                                                                                                                User interface (UI) testing lets you ensure that your app meets its functional requirements and achieves a high standard of quality such that it is more likely to be successfully adopted by users.

                                                                                                                                                                                                                                                                                                                One approach to UI testing is to simply have a human tester perform a set of user operations on the target app and verify that it is behaving correctly. However, this manual approach can be time-consuming, tedious, and error-prone. A more efficient approach is to write your UI tests such that user actions are performed in an automated way. The automated approach allows you to run your tests quickly and reliably in a repeatable manner.

                                                                                                                                                                                                                                                                                                                Note: It is strongly encouraged that you use Android Studio for building your test apps, because it provides project setup, library inclusion, and packaging conveniences. This class assumes you are using Android Studio.

                                                                                                                                                                                                                                                                                                                To automate UI tests with Android Studio, you implement your test code in a separate Android test folder (src/androidTest/java). The Android Plug-in for Gradle builds a test app based on your test code, then loads the test app on the same device as the target app. In your test code, you can use UI testing frameworks to simulate user interactions on the target app, in order to perform testing tasks that cover specific usage scenarios.

                                                                                                                                                                                                                                                                                                                For testing Android apps, you typically create these types of automated UI tests:

                                                                                                                                                                                                                                                                                                                • UI tests that span a single app: This type of test verifies that the target app behaves as expected when a user performs a specific action or enters a specific input in its activities. It allows you to check that the target app returns the correct UI output in response to user interactions in the app’s activities. UI testing frameworks like Espresso allow you to programmatically simulate user actions and test complex intra-app user interactions.
                                                                                                                                                                                                                                                                                                                • UI tests that span multiple apps: This type of test verifies the correct behavior of interactions between different user apps or between user apps and system apps. For example, you might want to test that your camera app shares images correctly with a 3rd-party social media app, or with the default Android Photos app. UI testing frameworks that support cross-app interactions, such as UI Automator, allow you to create tests for such scenarios.

                                                                                                                                                                                                                                                                                                                The lessons in this class teach you how to use the tools and APIs in the Android Testing Support Library to build these types of automated tests. Before you begin building tests using these APIs, you must install the Android Testing Support Library, as described in Downloading the Android Testing Support Library.

                                                                                                                                                                                                                                                                                                                Lessons

                                                                                                                                                                                                                                                                                                                Testing UI for a Single App
                                                                                                                                                                                                                                                                                                                Learn how to test UI in a single app by using the Espresso testing framework.
                                                                                                                                                                                                                                                                                                                Testing UI for Multiple Apps
                                                                                                                                                                                                                                                                                                                Learn how to test UI in multiple apps by using the UI Automator testing framework.
                                                                                                                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                Testing App Component Integrations | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                                                                                                                  Testing App Component Integrations

                                                                                                                                                                                                                                                                                                                  You should also read

                                                                                                                                                                                                                                                                                                                  If your app uses components that users do not directly interact with, such as a Service or Content Provider, you should validate that these components behave in a correct way with your app.

                                                                                                                                                                                                                                                                                                                  When developing such components, you should get into the habit of writing integration tests in order to validate the component's behavior when your app runs on a device or an emulator.

                                                                                                                                                                                                                                                                                                                  Note: Android does not provide a separate test case class for BroadcastReceiver. To verify that a BroadcastReceiver responds correctly, you can test the component that sends it an Intent object. Alternatively, you can create an instance of your BroadcastReceiver by calling InstrumentationRegistry.getTargetContext(), then call the BroadcastReceiver method that you want to test (usually, this is the onReceive() method).

                                                                                                                                                                                                                                                                                                                  This class teaches you to build automated integration tests using the testing APIs and tools that the Android platform provides.

                                                                                                                                                                                                                                                                                                                  Lessons

                                                                                                                                                                                                                                                                                                                  Testing Your Service
                                                                                                                                                                                                                                                                                                                  Learn how to build integration tests to verify that a service works correctly with your app.
                                                                                                                                                                                                                                                                                                                  Testing Your Content Provider
                                                                                                                                                                                                                                                                                                                  Learn how to build integration tests to verify that a content provider works correctly with your app.
                                                                                                                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                  Using Google Play to Distribute & Monetize | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                                                                                                                    Using Google Play to Distribute & Monetize

                                                                                                                                                                                                                                                                                                                    These classes focus on the business aspects of your app strategy, including techniques for distributing your app on Google Play and techniques for building revenue.

                                                                                                                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                    Selling In-app Products | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                                                                                                                      Selling In-app Products

                                                                                                                                                                                                                                                                                                                      Dependencies and prerequisites

                                                                                                                                                                                                                                                                                                                      • Android 2.2 or higher

                                                                                                                                                                                                                                                                                                                      You Should Also Read

                                                                                                                                                                                                                                                                                                                      In this class, you'll learn how to perform common In-app Billing operations from Android applications.

                                                                                                                                                                                                                                                                                                                      In-app billing is a service hosted on Google Play that lets you charge for digital content or for upgrades in your app. The In-app Billing API makes it easy for you to integrate In-app Billing into your applications. You can request product details from Google Play, issue orders for in-app products, and quickly retrieve ownership information based on users' purchase history. You can also query the Google Play service for details about in-app products, such as local pricing and availability. Google Play provides a checkout interface that makes user interactions with the In-app Billing service seamless, and provides a more intuitive experience to your users.

                                                                                                                                                                                                                                                                                                                      This class describes how to get started with the Version 3 API. To learn how to use the version 2 API, see Implementing In-App Billing (V2).

                                                                                                                                                                                                                                                                                                                      Lessons

                                                                                                                                                                                                                                                                                                                      Preparing Your In-app Billing Application
                                                                                                                                                                                                                                                                                                                      In this lesson, you will learn how to prepare your application to use the In-app Billing API and communicate with Google Play. You will also learn how to establish a connection to communicate with Google Play and verify that the In-app Billing API version that you are using is supported.
                                                                                                                                                                                                                                                                                                                      Establishing In-app Billing Products for Sale
                                                                                                                                                                                                                                                                                                                      In this lesson, you will learn how to specify the In-app Billing products for your app in Google Play and query the product details.
                                                                                                                                                                                                                                                                                                                      Purchase In-app Billing Products
                                                                                                                                                                                                                                                                                                                      In this lesson, you will learn how to purchase In-app Billing products, track consumption of purchased items, and query for details of purchased items.
                                                                                                                                                                                                                                                                                                                      Testing Your In-app Billing Application
                                                                                                                                                                                                                                                                                                                      In this lesson, you will learn how to test your application to ensure that In-app Billing is functioning correctly.
                                                                                                                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                      Preparing Your In-app Billing Application | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                                                                                                                        Preparing Your In-app Billing Application

                                                                                                                                                                                                                                                                                                                        Video

                                                                                                                                                                                                                                                                                                                        Implementing Freemium

                                                                                                                                                                                                                                                                                                                        Before you can start using the In-app Billing service, you'll need to add the library that contains the In-app Billing Version 3 API to your Android project. You also need to set the permissions for your application to communicate with Google Play. In addition, you'll need to establish a connection between your application and Google Play. You should also verify that the In-app Billing API version that you are using in your application is supported by Google Play.

                                                                                                                                                                                                                                                                                                                        Download the Sample Application

                                                                                                                                                                                                                                                                                                                        In this training class, you will use a reference implementation for the In-app Billing Version 3 API called the TrivialDrive sample application. The sample includes convenience classes to quickly set up the In-app Billing service, marshal and unmarshal data types, and handle In-app Billing requests from the main thread of your application.

                                                                                                                                                                                                                                                                                                                        To download the sample application:

                                                                                                                                                                                                                                                                                                                        1. Open Android Studio and then close any open projects until you are presented with the welcome screen.
                                                                                                                                                                                                                                                                                                                        2. Choose Import an Android code sample from the Quick Start list on the right side the window.
                                                                                                                                                                                                                                                                                                                        3. Type Trivial Drive into the search bar and select the Trivial Drive sample.
                                                                                                                                                                                                                                                                                                                        4. Follow the rest of the instructions in the Import Sample wizard to import the sample to a directory of your choosing. The sample code is in the TrivialDrive subdirectory of the repository.

                                                                                                                                                                                                                                                                                                                        Alternatively, you can use git to manually clone the repository from https://github.com/googlesamples/android-play-billing

                                                                                                                                                                                                                                                                                                                        Add Your Application to the Developer Console

                                                                                                                                                                                                                                                                                                                        The Google Play Developer Console is where you publish your In-app Billing application and manage the various digital goods that are available for purchase from your application. When you create a new application entry in the Developer Console, it automatically generates a public license key for your application. You will need this key to establish a trusted connection from your application to the Google Play servers. You only need to generate this key once per application, and don’t need to repeat these steps when you update the APK file for your application.

                                                                                                                                                                                                                                                                                                                        To add your application to the Developer Console:

                                                                                                                                                                                                                                                                                                                        1. Go to the Google Play Developer Console site and log in. You will need to register for a new developer account, if you have not registered previously. To sell in-app items, you also need to have a Google payments merchant account.
                                                                                                                                                                                                                                                                                                                        2. Click on Try the new design to access the preview version of the Developer Console, if you are not already logged on to that version.
                                                                                                                                                                                                                                                                                                                        3. In the All Applications tab, add a new application entry.
                                                                                                                                                                                                                                                                                                                          1. Click Add new application.
                                                                                                                                                                                                                                                                                                                          2. Enter a name for your new In-app Billing application.
                                                                                                                                                                                                                                                                                                                          3. Click Prepare Store Listing.
                                                                                                                                                                                                                                                                                                                        4. In the Services & APIs tab, find and make a note of the public license key that Google Play generated for your application. This is a Base64 string that you will need to include in your application code later.

                                                                                                                                                                                                                                                                                                                        Your application should now appear in the list of applications in Developer Console.

                                                                                                                                                                                                                                                                                                                        Add the In-app Billing Library

                                                                                                                                                                                                                                                                                                                        To use the In-app Billing Version 3 features, you must add the IInAppBillingService.aidl file to your Android project. This Android Interface Definition Language (AIDL) file defines the interface to the Google Play service.

                                                                                                                                                                                                                                                                                                                        You can find the IInAppBillingService.aidl file in the provided sample app. Depending on whether you are creating a new application or modifying an existing application, follow the instructions below to add the In-app Billing Library to your project.

                                                                                                                                                                                                                                                                                                                        New Project

                                                                                                                                                                                                                                                                                                                        To add the In-app Billing Version 3 library to your new In-app Billing project:

                                                                                                                                                                                                                                                                                                                        1. Copy the TrivialDrive sample files into your Android project.
                                                                                                                                                                                                                                                                                                                        2. Modify the package name in the files you copied to use the package name for your project. In Android Studio, you can use this shortcut: right-click the package name, then select Refactor > Rename.
                                                                                                                                                                                                                                                                                                                        3. Open the AndroidManifest.xml file and update the package attribute value to use the package name for your project.
                                                                                                                                                                                                                                                                                                                        4. Fix import statements as needed so that your project compiles correctly. In Android Studio, you can use this shortcut: press Ctrl+Shift+O in each file showing errors.
                                                                                                                                                                                                                                                                                                                        5. Modify the sample to create your own application. Remember to copy the Base64 public license key for your application from the Developer Console over to your MainActivity.java.

                                                                                                                                                                                                                                                                                                                        Existing Project

                                                                                                                                                                                                                                                                                                                        To add the In-app Billing Version 3 library to your existing In-app Billing project:

                                                                                                                                                                                                                                                                                                                        1. Copy the IInAppBillingService.aidl file to your Android project.
                                                                                                                                                                                                                                                                                                                          • In Android Studio: Create a directory named aidl under src/main, add a new package com.android.vending.billing in this directory, and import the IInAppBillingService.aidl file into this package.
                                                                                                                                                                                                                                                                                                                          • In other dev environments: Create the following directory /src/com/android/vending/billing and copy the IInAppBillingService.aidl file into this directory.
                                                                                                                                                                                                                                                                                                                        2. Build your application. You should see a generated file named IInAppBillingService.java in the /gen directory of your project.
                                                                                                                                                                                                                                                                                                                        3. Add the helper classes from the /util directory of the TrivialDrive sample to your project. Remember to change the package name declarations in those files accordingly so that your project compiles correctly.

                                                                                                                                                                                                                                                                                                                        Your project should now contain the In-app Billing Version 3 library.

                                                                                                                                                                                                                                                                                                                        Set the Billing Permission

                                                                                                                                                                                                                                                                                                                        Your app needs to have permission to communicate request and response messages to the Google Play’s billing service. To give your app the necessary permission, add this line in your AndroidManifest.xml manifest file:

                                                                                                                                                                                                                                                                                                                        <uses-permission android:name="com.android.vending.BILLING" />
                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                        Initiate a Connection with Google Play

                                                                                                                                                                                                                                                                                                                        You must bind your Activity to Google Play’s In-app Billing service to send In-app Billing requests to Google Play from your application. The convenience classes provided in the sample handles the binding to the In-app Billing service, so you don’t have to manage the network connection directly.

                                                                                                                                                                                                                                                                                                                        To set up synchronous communication with Google Play, create an IabHelper instance in your activity's onCreate method. In the constructor, pass in the Context for the activity, along with a string containing the public license key that was generated earlier by the Google Play Developer Console.

                                                                                                                                                                                                                                                                                                                        Security Recommendation: It is highly recommended that you do not hard-code the exact public license key string value as provided by Google Play. Instead, you can construct the whole public license key string at runtime from substrings, or retrieve it from an encrypted store, before passing it to the constructor. This approach makes it more difficult for malicious third-parties to modify the public license key string in your APK file.

                                                                                                                                                                                                                                                                                                                        IabHelper mHelper;
                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                        @Override
                                                                                                                                                                                                                                                                                                                        public void onCreate(Bundle savedInstanceState) {
                                                                                                                                                                                                                                                                                                                           // ...
                                                                                                                                                                                                                                                                                                                           String base64EncodedPublicKey;
                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                           // compute your public key and store it in base64EncodedPublicKey
                                                                                                                                                                                                                                                                                                                           mHelper = new IabHelper(this, base64EncodedPublicKey);
                                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                        Next, perform the service binding by calling the startSetup method on the IabHelper instance that you created. Pass the method an OnIabSetupFinishedListener instance, which is called once the IabHelper completes the asynchronous setup operation. As part of the setup process, the IabHelper also checks if the In-app Billing Version 3 API is supported by Google Play. If the API version is not supported, or if an error occured while establishing the service binding, the listener is notified and passed an IabResult object with the error message.

                                                                                                                                                                                                                                                                                                                        mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                                                                                                                                                                                                                                                                                                                           public void onIabSetupFinished(IabResult result) {
                                                                                                                                                                                                                                                                                                                              if (!result.isSuccess()) {
                                                                                                                                                                                                                                                                                                                                 // Oh noes, there was a problem.
                                                                                                                                                                                                                                                                                                                                 Log.d(TAG, "Problem setting up In-app Billing: " + result);
                                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                                                 // Hooray, IAB is fully set up!
                                                                                                                                                                                                                                                                                                                           }
                                                                                                                                                                                                                                                                                                                        });
                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                        If the setup completed successfully, you can now use the mHelper reference to communicate with the Google Play service. When your application is launched, it is a good practice to query Google Play to find out what in-app items are owned by a user. This is covered further in the Query Purchased Items section.

                                                                                                                                                                                                                                                                                                                        Important: Remember to unbind from the In-app Billing service when you are done with your activity. If you don’t unbind, the open service connection could cause your device’s performance to degrade. To unbind and free your system resources, call the IabHelper's dispose method when your Activity is destroyed.

                                                                                                                                                                                                                                                                                                                        @Override
                                                                                                                                                                                                                                                                                                                        public void onDestroy() {
                                                                                                                                                                                                                                                                                                                           super.onDestroy();
                                                                                                                                                                                                                                                                                                                           if (mHelper != null) mHelper.dispose();
                                                                                                                                                                                                                                                                                                                           mHelper = null;
                                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                        Establishing In-app Billing Products for Sale | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                        Most visited

                                                                                                                                                                                                                                                                                                                        Recently visited

                                                                                                                                                                                                                                                                                                                        navigation

                                                                                                                                                                                                                                                                                                                          Establishing In-app Billing Products for Sale

                                                                                                                                                                                                                                                                                                                          Before publishing your In-app Billing application, you'll need to define the product list of digital goods available for purchase in the Google Play Developer Console.

                                                                                                                                                                                                                                                                                                                          Specify In-app Products in Google Play

                                                                                                                                                                                                                                                                                                                          From the Developer Console, you can define product information for in-app products and associate the product list with your application.

                                                                                                                                                                                                                                                                                                                          To add new in-app products to your product list:

                                                                                                                                                                                                                                                                                                                          1. Build a signed APK file for your In-app Billing application. To learn how to build and sign your APK, see Building Your Application for Release. Make sure that you are using your final (not debug) certificate and private key to sign your application.
                                                                                                                                                                                                                                                                                                                          2. In the Developer Console, open the application entry that you created earlier.
                                                                                                                                                                                                                                                                                                                          3. Click on the APK tab then click on Upload new APK. Upload the signed APK file to the Developer Console. Don’t publish the app yet!
                                                                                                                                                                                                                                                                                                                          4. Navigate to the uploaded app listing, and click on In-app Products.
                                                                                                                                                                                                                                                                                                                          5. Click on the option to add a new product, then complete the form to specify the product information such as the item’s unique product ID (also called its SKU), description, price, and country availability. Note down the product ID since you might need this information to query purchase details in your application later.

                                                                                                                                                                                                                                                                                                                            Important: The In-app Billing Version 3 service only supports managed in-app products, so make sure that you specify that the purchase type is 'Managed' when you add new items to your product list in the Developer Console.

                                                                                                                                                                                                                                                                                                                          6. Once you have completed the form, activate the product so that your application can purchase it.

                                                                                                                                                                                                                                                                                                                            Warning: It may take up to 2-3 hours after uploading the APK for Google Play to recognize your updated APK version. If you try to test your application before your uploaded APK is recognized by Google Play, your application will receive a ‘purchase cancelled’ response with an error message “This version of the application is not enabled for In-app Billing.”

                                                                                                                                                                                                                                                                                                                          Query Items Available for Purchase

                                                                                                                                                                                                                                                                                                                          You can query Google Play to programmatically retrieve details of the in-app products that are associated with your application (such as the product’s price, title, description, and type). This is useful, for example, when you want to display a listing of unowned items that are still available for purchase to users.

                                                                                                                                                                                                                                                                                                                          Note: When making the query, you will need to specify the product IDs for the products explicitly. You can manually find the product IDs from the Developer Console by opening the In-app Products tab for your application. The product IDs are listed under the column labeled Name/ID.

                                                                                                                                                                                                                                                                                                                          To retrieve the product details, call queryInventoryAsync(boolean, List, QueryInventoryFinishedListener) on your IabHelper instance.

                                                                                                                                                                                                                                                                                                                          • The first input argument indicates whether product details should be retrieved (should be set to true).
                                                                                                                                                                                                                                                                                                                          • The List argument consists of one or more product IDs (also called SKUs) for the products that you want to query.
                                                                                                                                                                                                                                                                                                                          • Finally, the QueryInventoryFinishedListener argument specifies a listener is notified when the query operation has completed and handles the query response.
                                                                                                                                                                                                                                                                                                                          If you use the convenience classes provided in the sample, the classes will handle background thread management for In-app Billing requests, so you can safely make queries from the main thread of your application.

                                                                                                                                                                                                                                                                                                                          The following code shows how you can retrieve the details for two products with IDs SKU_APPLE and SKU_BANANA that you previously defined in the Developer Console.

                                                                                                                                                                                                                                                                                                                          List additionalSkuList = new List();
                                                                                                                                                                                                                                                                                                                          additionalSkuList.add(SKU_APPLE);
                                                                                                                                                                                                                                                                                                                          additionalSkuList.add(SKU_BANANA);
                                                                                                                                                                                                                                                                                                                          mHelper.queryInventoryAsync(true, additionalSkuList,
                                                                                                                                                                                                                                                                                                                             mQueryFinishedListener);
                                                                                                                                                                                                                                                                                                                          

                                                                                                                                                                                                                                                                                                                          If the query is successful, the query results are stored in an Inventory object that is passed back to the listener.

                                                                                                                                                                                                                                                                                                                          The following code shows how you can retrieve the item prices from the result set.

                                                                                                                                                                                                                                                                                                                          IabHelper.QueryInventoryFinishedListener 
                                                                                                                                                                                                                                                                                                                             mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
                                                                                                                                                                                                                                                                                                                             public void onQueryInventoryFinished(IabResult result, Inventory inventory)   
                                                                                                                                                                                                                                                                                                                             {
                                                                                                                                                                                                                                                                                                                                if (result.isFailure()) {
                                                                                                                                                                                                                                                                                                                                   // handle error
                                                                                                                                                                                                                                                                                                                                   return;
                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                 String applePrice =
                                                                                                                                                                                                                                                                                                                                    inventory.getSkuDetails(SKU_APPLE).getPrice();
                                                                                                                                                                                                                                                                                                                                 String bananaPrice =
                                                                                                                                                                                                                                                                                                                                    inventory.getSkuDetails(SKU_BANANA).getPrice();
                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                 // update the UI 
                                                                                                                                                                                                                                                                                                                             }
                                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                          This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                          This class requires API level or higher

                                                                                                                                                                                                                                                                                                                          This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                          For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                          Purchasing In-app Billing Products | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                          Most visited

                                                                                                                                                                                                                                                                                                                          Recently visited

                                                                                                                                                                                                                                                                                                                          navigation

                                                                                                                                                                                                                                                                                                                            Purchasing In-app Billing Products

                                                                                                                                                                                                                                                                                                                            This lesson teaches you to

                                                                                                                                                                                                                                                                                                                            1. Purchase an Item
                                                                                                                                                                                                                                                                                                                            2. Query Purchased Items
                                                                                                                                                                                                                                                                                                                            3. Consume a Purchase

                                                                                                                                                                                                                                                                                                                            You should also read

                                                                                                                                                                                                                                                                                                                            Once your application is connected to Google Play, you can initiate purchase requests for in-app products. Google Play provides a checkout interface for users to enter their payment method, so your application does not need to handle payment transactions directly.

                                                                                                                                                                                                                                                                                                                            When an item is purchased, Google Play recognizes that the user has ownership of that item and prevents the user from purchasing another item with the same product ID until it is consumed. You can control how the item is consumed in your application, and notify Google Play to make the item available for purchase again.

                                                                                                                                                                                                                                                                                                                            You can also query Google Play to quickly retrieve the list of purchases that were made by the user. This is useful, for example, when you want to restore the user's purchases when your user launches your app.

                                                                                                                                                                                                                                                                                                                            Purchase an Item

                                                                                                                                                                                                                                                                                                                            To start a purchase request from your app, call launchPurchaseFlow(Activity, String, int, OnIabPurchaseFinishedListener, String) on your IabHelper instance. You must make this call from the main thread of your Activity. Here’s an explaination of the launchPurchaseFlow method parameters:

                                                                                                                                                                                                                                                                                                                            • The first argument is the calling Activity.
                                                                                                                                                                                                                                                                                                                            • The second argument is the product ID (also called its SKU) of the item to purchase. Make sure that you are providing the ID and not the product name. You must have previously defined and activated the item in the Developer Console, otherwise it won’t be recognized.
                                                                                                                                                                                                                                                                                                                            • The third argument is a request code value. This value can be any positive integer. Google Play reurns this request code to the calling Activity’s onActivityResult along with the purchase response.
                                                                                                                                                                                                                                                                                                                            • The fourth argument is a listener that is notified when the purchase operation has completed and handles the purchase response from Google Play.
                                                                                                                                                                                                                                                                                                                            • The fifth argument contains a ‘developer payload’ string that you can use to send supplemental information about an order (it can be an empty string). Typically, this is used to pass in a string token that uniquely identifies this purchase request. If you specify a string value, Google Play returns this string along with the purchase response. Subsequently, when you make queries about this purchase, Google Play returns this string together with the purchase details.

                                                                                                                                                                                                                                                                                                                              Security Recommendation: It’s good practice to pass in a string that helps your application to identify the user who made the purchase, so that you can later verify that this is a legitimate purchase by that user. For consumable items, you can use a randomly generated string, but for non-consumable items you should use a string that uniquely identifies the user.

                                                                                                                                                                                                                                                                                                                            The following example shows how you can make a purchase request for a product with ID SKU_GAS, using an arbitrary value of 10001 for the request code, and an encoded developer payload string.

                                                                                                                                                                                                                                                                                                                            mHelper.launchPurchaseFlow(this, SKU_GAS, 10001,   
                                                                                                                                                                                                                                                                                                                               mPurchaseFinishedListener, "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
                                                                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                                                                            If the purchase order is successful, the response data from Google Play is stored in an Purchase object that is passed back to the listener.

                                                                                                                                                                                                                                                                                                                            The following example shows how you can handle the purchase response in the listener, depending on whether the purchase order was completed successfully, and whether the user purchased gas or a premium upgrade. In this example, gas is an in-app product that can be purchased multiple times, so you should consume the purchase to allow the user to buy it again. To learn how to consume purchases, see the Consuming Products section. The premium upgrade is a one-time purchase so you don’t need to consume it. It is good practice to update the UI immediately so that your users can see their newly purchased items.

                                                                                                                                                                                                                                                                                                                            IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener 
                                                                                                                                                                                                                                                                                                                               = new IabHelper.OnIabPurchaseFinishedListener() {
                                                                                                                                                                                                                                                                                                                               public void onIabPurchaseFinished(IabResult result, Purchase purchase) 
                                                                                                                                                                                                                                                                                                                               {
                                                                                                                                                                                                                                                                                                                                  if (result.isFailure()) {
                                                                                                                                                                                                                                                                                                                                     Log.d(TAG, "Error purchasing: " + result);
                                                                                                                                                                                                                                                                                                                                     return;
                                                                                                                                                                                                                                                                                                                                  }      
                                                                                                                                                                                                                                                                                                                                  else if (purchase.getSku().equals(SKU_GAS)) {
                                                                                                                                                                                                                                                                                                                                     // consume the gas and update the UI
                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                                  else if (purchase.getSku().equals(SKU_PREMIUM)) {
                                                                                                                                                                                                                                                                                                                                     // give user access to premium content and update the UI
                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                               }
                                                                                                                                                                                                                                                                                                                            };
                                                                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                                                                            Security Recommendation: When you receive the purchase response from Google Play, make sure to check the returned data signature, the orderId, and the developerPayload string in the Purchase object to make sure that you are getting the expected values. You should verify that the orderId is a unique value that you have not previously processed, and the developerPayload string matches the token that you sent previously with the purchase request. As a further security precaution, you should perform the verification on your own secure server.

                                                                                                                                                                                                                                                                                                                            Query Purchased Items

                                                                                                                                                                                                                                                                                                                            Upon a successful purchase, the user’s purchase data is cached locally by Google Play’s In-app Billing service. It is good practice to frequently query the In-app Billing service for the user’s purchases, for example whenever the app starts up or resumes, so that the user’s current in-app product ownership information is always reflected in your app.

                                                                                                                                                                                                                                                                                                                            To retrieve the user’s purchases from your app, call queryInventoryAsync(QueryInventoryFinishedListener) on your IabHelper instance. The QueryInventoryFinishedListener argument specifies a listener that is notified when the query operation has completed and handles the query response. It is safe to make this call fom your main thread.

                                                                                                                                                                                                                                                                                                                            mHelper.queryInventoryAsync(mGotInventoryListener);
                                                                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                                                                            If the query is successful, the query results are stored in an Inventory object that is passed back to the listener. The In-app Billing service returns only the purchases made by the user account that is currently logged in to the device.

                                                                                                                                                                                                                                                                                                                            IabHelper.QueryInventoryFinishedListener mGotInventoryListener 
                                                                                                                                                                                                                                                                                                                               = new IabHelper.QueryInventoryFinishedListener() {
                                                                                                                                                                                                                                                                                                                               public void onQueryInventoryFinished(IabResult result,
                                                                                                                                                                                                                                                                                                                                  Inventory inventory) {
                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                  if (result.isFailure()) {
                                                                                                                                                                                                                                                                                                                                    // handle error here
                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                                  else {
                                                                                                                                                                                                                                                                                                                                    // does the user have the premium upgrade?
                                                                                                                                                                                                                                                                                                                                    mIsPremium = inventory.hasPurchase(SKU_PREMIUM);        
                                                                                                                                                                                                                                                                                                                                    // update UI accordingly
                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                               }
                                                                                                                                                                                                                                                                                                                            };
                                                                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                                                                            Consume a Purchase

                                                                                                                                                                                                                                                                                                                            You can use the In-app Billing Version 3 API to track the ownership of purchased items in Google Play. Once an item is purchased, it is considered to be "owned" and cannot be purchased again from Google Play while in that state. You must send a consumption request for the item before Google Play makes it available for purchase again. All managed in-app products are consumable. How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or replenishable game tokens). You would typically not want to implement consumption for products that are purchased once and provide a permanent effect (for example, a premium upgrade).

                                                                                                                                                                                                                                                                                                                            It's your responsibility to control and track how the in-app product is provisioned to the user. For example, if the user purchased in-game currency, you should update the player's inventory with the amount of currency purchased.

                                                                                                                                                                                                                                                                                                                            Security Recommendation: You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.

                                                                                                                                                                                                                                                                                                                            To record a purchase consumption, call consumeAsync(Purchase, OnConsumeFinishedListener) on your IabHelper instance. The first argument that the method takes is the Purchase object representing the item to consume. The second argument is a OnConsumeFinishedListener that is notified when the consumption operation has completed and handles the consumption response from Google Play. It is safe to make this call fom your main thread.

                                                                                                                                                                                                                                                                                                                            In this example, you want to consume the gas item that the user has previously purchased in your app.

                                                                                                                                                                                                                                                                                                                            mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), 
                                                                                                                                                                                                                                                                                                                               mConsumeFinishedListener);
                                                                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                                                                            The following example shows how to implement the OnConsumeFinishedListener.

                                                                                                                                                                                                                                                                                                                            IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
                                                                                                                                                                                                                                                                                                                               new IabHelper.OnConsumeFinishedListener() {
                                                                                                                                                                                                                                                                                                                               public void onConsumeFinished(Purchase purchase, IabResult result) {
                                                                                                                                                                                                                                                                                                                                  if (result.isSuccess()) {
                                                                                                                                                                                                                                                                                                                                     // provision the in-app purchase to the user
                                                                                                                                                                                                                                                                                                                                     // (for example, credit 50 gold coins to player's character)
                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                                  else {
                                                                                                                                                                                                                                                                                                                                     // handle error
                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                               }
                                                                                                                                                                                                                                                                                                                            };
                                                                                                                                                                                                                                                                                                                            

                                                                                                                                                                                                                                                                                                                            Check for Consumable Items on Startup

                                                                                                                                                                                                                                                                                                                            It’s important to check for consumable items when the user starts up your application. Typically, you would first query the In-app Billing service for the items purchased by the user (via queryInventoryAsync), then get the consumable Purchase objects from the Inventory. If your application detects that are any consumable items that are owned by the user, you should send a consumption request to Google Play immediately and provision the item to the user. See the TrivialDrive sample for an example of how to implement this checking at startup.

                                                                                                                                                                                                                                                                                                                            This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                            This class requires API level or higher

                                                                                                                                                                                                                                                                                                                            This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                            For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                            Testing Your In-app Billing Application | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                            Most visited

                                                                                                                                                                                                                                                                                                                            Recently visited

                                                                                                                                                                                                                                                                                                                            navigation

                                                                                                                                                                                                                                                                                                                              Testing Your In-app Billing Application

                                                                                                                                                                                                                                                                                                                              This lesson teaches you to

                                                                                                                                                                                                                                                                                                                              1. Test with Static Responses
                                                                                                                                                                                                                                                                                                                              2. Test with Your Own Product IDs

                                                                                                                                                                                                                                                                                                                              You should also read

                                                                                                                                                                                                                                                                                                                              To ensure that In-app Billing is functioning correctly in your application, you should test the test the application before you publish it on Google Play. Early testing also helps to ensure that the user flow for purchasing in-app items is not confusing or slow, and that users can see their newly purchased items in a timely way.

                                                                                                                                                                                                                                                                                                                              Test with Static Responses

                                                                                                                                                                                                                                                                                                                              Test your In-app Billing application with static responses by using Google Play’s reserved product IDs.By using reserved product IDs instead of actual product IDs, you can test the purchase flow without specifying an actual payment method or transferring money. To learn more about the reserved product IDs, see Testing In-app Billing.

                                                                                                                                                                                                                                                                                                                              Test with Your Own Product IDs

                                                                                                                                                                                                                                                                                                                              Because Google Play does not allow you to use your developer account to directly purchase in-app products that you have created yourself, you'll need to create test acccounts under your developer account profile. To create a test account, simply enter a valid Google email address. Users with these test accounts will then be able to make in-app-billing purchases from uploaded, unpublished applications that you manage.

                                                                                                                                                                                                                                                                                                                              To test your In-app Billing Version 3 application using your own product IDs:

                                                                                                                                                                                                                                                                                                                              1. In the Developer Console, add one or more tester accounts to the developer account that you are using to publish your application.
                                                                                                                                                                                                                                                                                                                                1. Login to the Developer Console with your developer account.
                                                                                                                                                                                                                                                                                                                                2. Click Settings > Account details, then in the License Testing section, add the Google email addresses for your tester accounts.
                                                                                                                                                                                                                                                                                                                              2. Build a signed APK file for your In-app Billing application. To learn how to build and sign your APK, see Building Your Application for Release. Make sure that you are using your final (not debug) certificate and private key to sign your application.
                                                                                                                                                                                                                                                                                                                              3. Make sure that you have uploaded the signed APK for your application to the Developer Console, and associated one or more in-app products with your application. You don't need to publish the application on Google Play to test it.

                                                                                                                                                                                                                                                                                                                                Warning: It may take up to 2-3 hours after uploading the APK for Google Play to recognize your updated APK version. If you try to test your application before your uploaded APK is recognized by Google Play, your application will receive a ‘purchase cancelled’ response with an error message “This version of the application is not enabled for In-app Billing.”

                                                                                                                                                                                                                                                                                                                              4. Install the APK file to your physical test device by using the adb tool. To learn how to install the application, see Running on a Device. Make sure that:
                                                                                                                                                                                                                                                                                                                                • Your test device is running on Android SDK Version 2.2 (API level 8) or higher, and is installed with Google Play client Version 3.9.16 or higher.
                                                                                                                                                                                                                                                                                                                                • The android:versionCode and android:versionName attributes values in the AndroidManifest.xml of the application that you are installing matches the values of your APK in the Developer Console.
                                                                                                                                                                                                                                                                                                                                • Your application is signed with the same certificate that you used for the APK that you uploaded to the Developer Console, before installing it on your device.
                                                                                                                                                                                                                                                                                                                              5. Login to the test device by using a tester account. Test your In-app Billing application by purchasing a few items, and fix any issues that you encounter. To learn more about how you can perform large-scale testing of your In-app Billing app, see Test Purchases (In-app Billing Sandbox).

                                                                                                                                                                                                                                                                                                                              This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                              This class requires API level or higher

                                                                                                                                                                                                                                                                                                                              This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                              For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                              Maintaining Multiple APKs | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                              Most visited

                                                                                                                                                                                                                                                                                                                              Recently visited

                                                                                                                                                                                                                                                                                                                              navigation

                                                                                                                                                                                                                                                                                                                                Maintaining Multiple APKs

                                                                                                                                                                                                                                                                                                                                Dependencies and prerequisites

                                                                                                                                                                                                                                                                                                                                • Android 1.0 and higher
                                                                                                                                                                                                                                                                                                                                • You must have an Google Play publisher account

                                                                                                                                                                                                                                                                                                                                You should also read

                                                                                                                                                                                                                                                                                                                                Multiple APK support is a feature of Google Play that allows you to publish multiple APKs under the same application listing. Each APK is a complete instance of your application, optimized to target specific device configurations. Each APK can target a specific set of GL textures, API levels, screen sizes, or some combination thereof.

                                                                                                                                                                                                                                                                                                                                This class shows you how to write your multiple APK application using any one of these configuration variables. Each lesson covers basics about how to organize your codebase and target the right devices, as well as the smart way to avoid pitfalls such as unnecessary redundancy across your codebase, and making mistakes in your manifest that could render an APK invisible to all devices on Google Play. By going through any of these lessons, you'll know how to develop multiple APKs the smart way, make sure they're targeting the devices you want them to, and know how to catch mistakes before your app goes live.

                                                                                                                                                                                                                                                                                                                                Lessons

                                                                                                                                                                                                                                                                                                                                Creating Multiple APKs for Different API Levels
                                                                                                                                                                                                                                                                                                                                Learn how to target different versions of the Android platform using multiple APKs. Also learn how to organize your codebase, what to do with your manifest, and how to investigate your APK configuration using the aapt tool before pushing live.
                                                                                                                                                                                                                                                                                                                                Creating Multiple APKs for Different Screen Sizes
                                                                                                                                                                                                                                                                                                                                Learn how to target Android devices by screen size using multiple APKs. Also learn how to organize your codebase, what to do with your manifest, and how to investigate your APK configuration using the aapt tool before pushing live.
                                                                                                                                                                                                                                                                                                                                Creating Multiple APKs for Different GL Textures
                                                                                                                                                                                                                                                                                                                                Learn how to target Android devices based on their support for GL texture. Also learn how to organize your codebase, what to do with your manifest, and how to investigate your APK configuration using the aapt tool before pushing live.
                                                                                                                                                                                                                                                                                                                                Creating Multiple APKs with 2+ Dimensions
                                                                                                                                                                                                                                                                                                                                Learn how to target different Android devices based on more than one configuration variable (screen size, API version, GL texture). Examples in the lesson target using a combination of API level and screen size. Also learn how to organize your codebase, what to do with your manifest, and how to investigate your APK configuration using the aapt tool before pushing live.
                                                                                                                                                                                                                                                                                                                                This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                                This class requires API level or higher

                                                                                                                                                                                                                                                                                                                                This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                                For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                                Creating Multiple APKs for Different API Levels | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                                Most visited

                                                                                                                                                                                                                                                                                                                                Recently visited

                                                                                                                                                                                                                                                                                                                                navigation

                                                                                                                                                                                                                                                                                                                                  Creating Multiple APKs for Different API Levels

                                                                                                                                                                                                                                                                                                                                  When developing your Android application to take advantage of multiple APKs on Google Play, it’s important to adopt some good practices from the get-go, and prevent unnecessary headaches further into the development process. This lesson shows you how to create multiple APKs of your app, each covering a slightly different range of API levels. You will also gain some tools necessary to make maintaining a multiple APK codebase as painless as possible.

                                                                                                                                                                                                                                                                                                                                  Confirm You Need Multiple APKs

                                                                                                                                                                                                                                                                                                                                  When trying to create an application that works across multiple generations of the Android platform, naturally you want your application to take advantage of new features on new devices, without sacrificing backwards compatibility. It may seem at the outset as though multiple APK support is the best solution, but this often isn’t the case. The Using Single APK Instead section of the multiple APK developer guide includes some useful information on how to accomplish this with a single APK, including use of our support library. You can also learn how to write code that runs only at certain API levels in a single APK, without resorting to computationally expensive techniques like reflection from this article.

                                                                                                                                                                                                                                                                                                                                  If you can manage it, confining your application to a single APK has several advantages, including:

                                                                                                                                                                                                                                                                                                                                  • Publishing and testing are easier
                                                                                                                                                                                                                                                                                                                                  • There’s only one codebase to maintain
                                                                                                                                                                                                                                                                                                                                  • Your application can adapt to device configuration changes
                                                                                                                                                                                                                                                                                                                                  • App restore across devices just works
                                                                                                                                                                                                                                                                                                                                  • You don’t have to worry about market preference, behavior from "upgrades" from one APK to the next, or which APK goes with which class of devices

                                                                                                                                                                                                                                                                                                                                  The rest of this lesson assumes that you’ve researched the topic, studiously absorbed the material in the resources linked, and determined that multiple APKs are the right path for your application.

                                                                                                                                                                                                                                                                                                                                  Chart Your Requirements

                                                                                                                                                                                                                                                                                                                                  Start off by creating a simple chart to quickly determine how many APKs you need, and what API range each APK covers. For handy reference, the Platform Versions page of the Android Developer website provides data about the relative number of active devices running a given version of the Android platform. Also, although it sounds easy at first, keeping track of which set of API levels each APK is going to target gets difficult rather quickly, especially if there’s going to be some overlap (there often is). Fortunately, it’s easy to chart out your requirements quickly, easily, and have an easy reference for later.

                                                                                                                                                                                                                                                                                                                                  In order to create your multiple APK chart, start out with a row of cells representing the various API levels of the Android platform. Throw an extra cell at the end to represent future versions of Android.

                                                                                                                                                                                                                                                                                                                                  3 4 5 6 7 8 9 10 11 12 13 +

                                                                                                                                                                                                                                                                                                                                  Now just color in the chart such that each color represents an APK. Here’s one example of how you might apply each APK to a certain range of API levels.

                                                                                                                                                                                                                                                                                                                                  3 4 5 6 7 8 9 10 11 12 13 +

                                                                                                                                                                                                                                                                                                                                  Once you’ve created this chart, distribute it to your team. Team communication on your project just got immediately simpler, since instead of asking "How’s the APK for API levels 3 to 6, er, you know, the Android 1.x one. How’s that coming along?" You can simply say "How’s the Blue APK coming along?"

                                                                                                                                                                                                                                                                                                                                  Put All Common Code and Resources in a Library Project

                                                                                                                                                                                                                                                                                                                                  Whether you’re modifying an existing Android application or starting one from scratch, this is the first thing that you should do to the codebase, and by the far the most important. Everything that goes into the library project only needs to be updated once (think language-localized strings, color themes, bugs fixed in shared code), which improves your development time and reduces the likelihood of mistakes that could have been easily avoided.

                                                                                                                                                                                                                                                                                                                                  Note: While the implementation details of how to create and include library projects are beyond the scope of this lesson, you can get up to speed by reading Create an Android Library.

                                                                                                                                                                                                                                                                                                                                  If you’re converting an existing application to use multiple APK support, scour your codebase for every localized string file, list of values, theme colors, menu icons and layout that isn’t going to change across APKs, and put it all in the library project. Code that isn’t going to change much should also go in the library project. You’ll likely find yourself extending these classes to add a method or two from APK to APK.

                                                                                                                                                                                                                                                                                                                                  If, on the other hand, you’re creating the application from scratch, try as much as possible to write code in the library project first, then only move it down to an individual APK if necessary. This is much easier to manage in the long run than adding it to one, then another, then another, then months later trying to figure out whether this blob can be moved up to the library section without screwing anything up.

                                                                                                                                                                                                                                                                                                                                  Create New APK Projects

                                                                                                                                                                                                                                                                                                                                  There should be a separate Android project for each APK you’re going to release. For easy organization, place the library project and all related APK projects under the same parent folder. Also remember that each APK needs to have the same package name, although they don’t necessarily need to share the package name with the library. If you were to have 3 APKs following the scheme described earlier, your root directory might look like this:

                                                                                                                                                                                                                                                                                                                                  alexlucas:~/code/multi-apks-root$ ls
                                                                                                                                                                                                                                                                                                                                  foo-blue
                                                                                                                                                                                                                                                                                                                                  foo-green
                                                                                                                                                                                                                                                                                                                                  foo-lib
                                                                                                                                                                                                                                                                                                                                  foo-red
                                                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                                                  Once the projects are created, add the library project as a reference to each APK project. If possible, define your starting Activity in the library project, and extend that Activity in your APK project. Having a starting activity defined in the library project gives you a chance to put all your application initialization in one place, so that each individual APK doesn’t have to re-implement "universal" tasks like initializing Analytics, running licensing checks, and any other initialization procedures that don’t change much from APK to APK.

                                                                                                                                                                                                                                                                                                                                  Adjust the Manifests

                                                                                                                                                                                                                                                                                                                                  When a user downloads an application which uses multiple APKs through Google Play, the correct APK to use is chosen using two simple rules:

                                                                                                                                                                                                                                                                                                                                  • The manifest has to show that particular APK is eligible
                                                                                                                                                                                                                                                                                                                                  • Of the eligible APKs, highest version number wins

                                                                                                                                                                                                                                                                                                                                  By way of example, let’s take the set of multiple APKs described earlier, and assume that we haven’t set a max API level for any of the APKs. Taken individually, the possible range of each APK would look like this:

                                                                                                                                                                                                                                                                                                                                  3 4 5 6 7 8 9 10 11 12 13 +
                                                                                                                                                                                                                                                                                                                                  3 4 5 6 7 8 9 10 11 12 13 +
                                                                                                                                                                                                                                                                                                                                  3 4 5 6 7 8 9 10 11 12 13 +

                                                                                                                                                                                                                                                                                                                                  Because it is required that an APK with a higher minSdkVersion also have a higher version code, we know that in terms of versionCode values, red ≥ green ≥ blue. Therefore we can effectively collapse the chart to look like this:

                                                                                                                                                                                                                                                                                                                                  3 4 5 6 7 8 9 10 11 12 13 +

                                                                                                                                                                                                                                                                                                                                  Now, let’s further assume that the Red APK has some requirement on it that the other two don’t. Filters on Google Play page of the Android Developer guide has a whole list of possible culprits. For the sake of example, let’s assume that red requires a front-facing camera. In fact, the entire point of the red APK is to combine the front-facing camera with sweet new functionality that was added in API 11. But, it turns out, not all devices that support API 11 even HAVE front-facing cameras! The horror!

                                                                                                                                                                                                                                                                                                                                  Fortunately, if a user is browsing Google Play from one such device, Google Play will look at the manifest, see that Red lists the front-facing camera as a requirement, and quietly ignore it, having determined that Red and that device are not a match made in digital heaven. It will then see that Green is not only forward-compatible with devices with API 11 (since no maxSdkVersion was defined), but also doesn’t care whether or not there’s a front-facing camera! The app can still be downloaded from Google Play by the user, because despite the whole front-camera mishap, there was still an APK that supported that particular API level.

                                                                                                                                                                                                                                                                                                                                  In order to keep all your APKs on separate "tracks", it’s important to have a good version code scheme. The recommended one can be found on the Version Codes area of our developer guide. Since the example set of APKs is only dealing with one of 3 possible dimensions, it would be sufficient to separate each APK by 1000, set the first couple digits to the minSdkVersion for that particular APK, and increment from there. This might look like:

                                                                                                                                                                                                                                                                                                                                  Blue: 03001, 03002, 03003, 03004...
                                                                                                                                                                                                                                                                                                                                  Green: 07001, 07002, 07003, 07004...
                                                                                                                                                                                                                                                                                                                                  Red:11001, 11002, 11003, 11004...

                                                                                                                                                                                                                                                                                                                                  Putting this all together, your Android Manifests would likely look something like the following:

                                                                                                                                                                                                                                                                                                                                  Blue:

                                                                                                                                                                                                                                                                                                                                  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                      android:versionCode="03001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                      <uses-sdk android:minSdkVersion="3" />
                                                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                                                  Green:

                                                                                                                                                                                                                                                                                                                                  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                      android:versionCode="07001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                      <uses-sdk android:minSdkVersion="7" />
                                                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                                                  Red:

                                                                                                                                                                                                                                                                                                                                  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                      android:versionCode="11001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                      <uses-sdk android:minSdkVersion="11" />
                                                                                                                                                                                                                                                                                                                                      ...
                                                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                                                  Go Over Pre-launch Checklist

                                                                                                                                                                                                                                                                                                                                  Before uploading to Google Play, double-check the following items. Remember that these are specifically relevant to multiple APKs, and in no way represent a complete checklist for all applications being uploaded to Google Play.

                                                                                                                                                                                                                                                                                                                                  • All APKs must have the same package name
                                                                                                                                                                                                                                                                                                                                  • All APKs must be signed with the same certificate
                                                                                                                                                                                                                                                                                                                                  • If the APKs overlap in platform version, the one with the higher minSdkVersion must have a higher version code
                                                                                                                                                                                                                                                                                                                                  • Double check your manifest filters for conflicting information (an APK that only supports cupcake on XLARGE screens isn’t going to be seen by anybody)
                                                                                                                                                                                                                                                                                                                                  • Each APK's manifest must be unique across at least one of supported screen, openGL texture, or platform version
                                                                                                                                                                                                                                                                                                                                  • Try to test each APK on at least one device. Barring that, you have one of the most customizable device emulators in the business sitting on your development machine. Go nuts!

                                                                                                                                                                                                                                                                                                                                  It’s also worth inspecting the compiled APK before pushing to market, to make sure there aren’t any surprises that could hide your application on Google Play. This is actually quite simple using the "aapt" tool. Aapt (the Android Asset Packaging Tool) is part of the build process for creating and packaging your Android applications, and is also a very handy tool for inspecting them.

                                                                                                                                                                                                                                                                                                                                  >aapt dump badging
                                                                                                                                                                                                                                                                                                                                  package: name='com.example.hello' versionCode='1' versionName='1.0'
                                                                                                                                                                                                                                                                                                                                  sdkVersion:'11'
                                                                                                                                                                                                                                                                                                                                  uses-permission:'android.permission.SEND_SMS'
                                                                                                                                                                                                                                                                                                                                  application-label:'Hello'
                                                                                                                                                                                                                                                                                                                                  application-icon-120:'res/drawable-ldpi/icon.png'
                                                                                                                                                                                                                                                                                                                                  application-icon-160:'res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                  application-icon-240:'res/drawable-hdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                  application: label='Hello' icon='res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                  launchable-activity: name='com.example.hello.HelloActivity'  label='Hello' icon=''
                                                                                                                                                                                                                                                                                                                                  uses-feature:'android.hardware.telephony'
                                                                                                                                                                                                                                                                                                                                  uses-feature:'android.hardware.touchscreen'
                                                                                                                                                                                                                                                                                                                                  main
                                                                                                                                                                                                                                                                                                                                  supports-screens: 'small' 'normal' 'large' 'xlarge'
                                                                                                                                                                                                                                                                                                                                  supports-any-density: 'true'
                                                                                                                                                                                                                                                                                                                                  locales: '--_--'
                                                                                                                                                                                                                                                                                                                                  densities: '120' '160' '240'
                                                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                                                  When you examine aapt output, be sure to check that you don’t have conflicting values for supports-screens and compatible-screens, and that you don’t have unintended "uses-feature" values that were added as a result of permissions you set in the manifest. In the example above, the APK won’t be visible to very many devices.

                                                                                                                                                                                                                                                                                                                                  Why? By adding the required permission SEND_SMS, the feature requirement of android.hardware.telephony was implicitly added. Since API 11 is Honeycomb (the version of Android optimized specifically for tablets), and no Honeycomb devices have telephony hardware in them, Google Play will filter out this APK in all cases, until future devices come along which are higher in API level AND possess telephony hardware.

                                                                                                                                                                                                                                                                                                                                  Fortunately this is easily fixed by adding the following to your manifest:

                                                                                                                                                                                                                                                                                                                                  <uses-feature android:name="android.hardware.telephony" android:required="false" />
                                                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                                                  The android.hardware.touchscreen requirement is also implicitly added. If you want your APK to be visible on TVs which are non-touchscreen devices you should add the following to your manifest:

                                                                                                                                                                                                                                                                                                                                  <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
                                                                                                                                                                                                                                                                                                                                  

                                                                                                                                                                                                                                                                                                                                  Once you’ve completed the pre-launch checklist, upload your APKs to Google Play. It may take a bit for the application to show up when browsing Google Play, but when it does, perform one last check. Download the application onto any test devices you may have, to make sure that the APKs are targeting the intended devices. Congratulations, you’re done!

                                                                                                                                                                                                                                                                                                                                  This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                                  This class requires API level or higher

                                                                                                                                                                                                                                                                                                                                  This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                                  For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                                  Creating Multiple APKs for Different Screen Sizes | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                                  Most visited

                                                                                                                                                                                                                                                                                                                                  Recently visited

                                                                                                                                                                                                                                                                                                                                  navigation

                                                                                                                                                                                                                                                                                                                                    Creating Multiple APKs for Different Screen Sizes

                                                                                                                                                                                                                                                                                                                                    When developing your Android application to take advantage of multiple APKs on Google Play, it’s important to adopt some good practices from the get-go, and prevent unnecessary headaches further into the development process. This lesson shows you how to create multiple APKs of your app, each covering a different class of screen size. You will also gain some tools necessary to make maintaining a multiple APK codebase as painless as possible.

                                                                                                                                                                                                                                                                                                                                    Confirm You Need Multiple APKs

                                                                                                                                                                                                                                                                                                                                    When trying to create an application that works across multiple sizes of Android devices, naturally you want your application to take advantage of all the available space on larger devices, without sacrificing compatibility or usability on the smaller screens. It may seem at the outset as though multiple APK support is the best solution, but this often isn’t the case. The Using Single APK Instead section of the multiple APK developer guide includes some useful information on how to accomplish this with a single APK, including use of our support library. You should also read the guide to supporting multiple screens, and there’s even a support library you can download using the Android SDK, which lets you use fragments on pre-Honeycomb devices (making multiple-screen support in a single APK much easier).

                                                                                                                                                                                                                                                                                                                                    If you can manage it, confining your application to a single APK has several advantages, including:

                                                                                                                                                                                                                                                                                                                                    • Publishing and testing are easier
                                                                                                                                                                                                                                                                                                                                    • There’s only one codebase to maintain
                                                                                                                                                                                                                                                                                                                                    • Your application can adapt to device configuration changes
                                                                                                                                                                                                                                                                                                                                    • App restore across devices just works
                                                                                                                                                                                                                                                                                                                                    • You don’t have to worry about market preference, behavior from "upgrades" from one APK to the next, or which APK goes with which class of devices

                                                                                                                                                                                                                                                                                                                                    The rest of this lesson assumes that you’ve researched the topic, studiously absorbed the material in the resources linked, and determined that multiple APKs are the right path for your application.

                                                                                                                                                                                                                                                                                                                                    Chart Your Requirements

                                                                                                                                                                                                                                                                                                                                    Start off by creating a simple chart to quickly determine how many APKs you need, and what screen size(s) each APK covers. Fortunately, it’s easy to chart out your requirements quickly and easily, and have a reference for later. Start out with a row of cells representing the various screen sizes available on the Android platform.

                                                                                                                                                                                                                                                                                                                                    small normal large xlarge

                                                                                                                                                                                                                                                                                                                                    Now just color in the chart such that each color represents an APK. Here’s one example of how you might apply each APK to a certain range of screen sizes.

                                                                                                                                                                                                                                                                                                                                    small normal large xlarge

                                                                                                                                                                                                                                                                                                                                    Depending on your needs, you could also have two APKs, "small and everything else" or "xlarge and everything else". Coloring in the chart also makes intra-team communication easier—You can now simply refer to each APK as "blue", "green", or "red", no matter how many different screen types it covers.

                                                                                                                                                                                                                                                                                                                                    Put All Common Code and Resources in a Library Project

                                                                                                                                                                                                                                                                                                                                    Whether you’re modifying an existing Android application or starting one from scratch, this is the first thing that you should do to the codebase, and by the far the most important. Everything that goes into the library project only needs to be updated once (think language-localized strings, color themes, bugs fixed in shared code), which improves your development time and reduces the likelihood of mistakes that could have been easily avoided.

                                                                                                                                                                                                                                                                                                                                    Note: While the implementation details of how to create and include library projects are beyond the scope of this lesson, you can get up to speed by reading Create an Android Library.

                                                                                                                                                                                                                                                                                                                                    If you’re converting an existing application to use multiple APK support, scour your codebase for every localized string file, list of values, theme colors, menu icons and layout that isn’t going to change across APKs, and put it all in the library project. Code that isn’t going to change much should also go in the library project. You’ll likely find yourself extending these classes to add a method or two from APK to APK.

                                                                                                                                                                                                                                                                                                                                    If, on the other hand, you’re creating the application from scratch, try as much as possible to write code in the library project first, then only move it down to an individual APK if necessary. This is much easier to manage in the long run than adding it to one, then another, then another, then months later trying to figure out whether this blob can be moved up to the library section without screwing anything up.

                                                                                                                                                                                                                                                                                                                                    Create New APK Projects

                                                                                                                                                                                                                                                                                                                                    There should be a separate Android project for each APK you’re going to release. For easy organization, place the library project and all related APK projects under the same parent folder. Also remember that each APK needs to have the same package name, although they don’t necessarily need to share the package name with the library. If you were to have 3 APKs following the scheme described earlier, your root directory might look like this:

                                                                                                                                                                                                                                                                                                                                    alexlucas:~/code/multi-apks-root$ ls
                                                                                                                                                                                                                                                                                                                                    foo-blue
                                                                                                                                                                                                                                                                                                                                    foo-green
                                                                                                                                                                                                                                                                                                                                    foo-lib
                                                                                                                                                                                                                                                                                                                                    foo-red
                                                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                                                    Once the projects are created, add the library project as a reference to each APK project. If possible, define your starting Activity in the library project, and extend that Activity in your APK project. Having a starting activity defined in the library project gives you a chance to put all your application initialization in one place, so that each individual APK doesn’t have to re-implement "universal" tasks like initializing Analytics, running licensing checks, and any other initialization procedures that don’t change much from APK to APK.

                                                                                                                                                                                                                                                                                                                                    Adjust the Manifests

                                                                                                                                                                                                                                                                                                                                    When a user downloads an application which uses multiple APKs through Google Play, the correct APK to use is chosen using two simple rules:

                                                                                                                                                                                                                                                                                                                                    • The manifest has to show that particular APK is eligible
                                                                                                                                                                                                                                                                                                                                    • Of the eligible APKs, highest version number wins

                                                                                                                                                                                                                                                                                                                                    By way of example, let’s take the set of multiple APKs described earlier, and assume that each APK has been set to support all screen sizes larger than its "target" screen size. Taken individually, the possible range of each APK would look like this:

                                                                                                                                                                                                                                                                                                                                    small normal large xlarge
                                                                                                                                                                                                                                                                                                                                    small normal large xlarge
                                                                                                                                                                                                                                                                                                                                    small normal large xlarge

                                                                                                                                                                                                                                                                                                                                    However, by using the "highest version number wins" rule, if we set the versionCode attribute in each APK such that red ≥ green ≥ blue, the chart effectively collapses down to this:

                                                                                                                                                                                                                                                                                                                                    small normal large xlarge

                                                                                                                                                                                                                                                                                                                                    Now, let’s further assume that the Red APK has some requirement on it that the other two don’t. The Filters on Google Play page of the Android Developer guide has a whole list of possible culprits. For the sake of example, let’s assume that red requires a front-facing camera. In fact, the entire point of the red APK is to use the extra available screen space to do entertaining things with that front-facing camera. But, it turns out, not all xlarge devices even HAVE front-facing cameras! The horror!

                                                                                                                                                                                                                                                                                                                                    Fortunately, if a user is browsing Google Play from one such device, Google Play will look at the manifest, see that Red lists the front-facing camera as a requirement, and quietly ignore it, having determined that Red and that device are not a match made in digital heaven. It will then see that Green is not only compatible with xlarge devices, but also doesn’t care whether or not there’s a front-facing camera! The app can still be downloaded from Google Play by the user, because despite the whole front-camera mishap, there was still an APK that supported that particular screen size.

                                                                                                                                                                                                                                                                                                                                    In order to keep all your APKs on separate "tracks", it’s important to have a good version code scheme. The recommended one can be found on the Version Codes area of our developer guide. Since the example set of APKs is only dealing with one of 3 possible dimensions, it would be sufficient to separate each APK by 1000 and increment from there. This might look like:

                                                                                                                                                                                                                                                                                                                                    Blue: 1001, 1002, 1003, 1004...
                                                                                                                                                                                                                                                                                                                                    Green: 2001, 2002, 2003, 2004...
                                                                                                                                                                                                                                                                                                                                    Red:3001, 3002, 3003, 3004...

                                                                                                                                                                                                                                                                                                                                    Putting this all together, your Android Manifests would likely look something like the following:

                                                                                                                                                                                                                                                                                                                                    Blue:

                                                                                                                                                                                                                                                                                                                                    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                        android:versionCode="1001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                        <supports-screens android:smallScreens="true"
                                                                                                                                                                                                                                                                                                                                            android:normalScreens="true"
                                                                                                                                                                                                                                                                                                                                            android:largeScreens="true"
                                                                                                                                                                                                                                                                                                                                            android:xlargeScreens="true" />
                                                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                                                    Green:

                                                                                                                                                                                                                                                                                                                                    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                        android:versionCode="2001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                        <supports-screens android:smallScreens="false"
                                                                                                                                                                                                                                                                                                                                            android:normalScreens="false"
                                                                                                                                                                                                                                                                                                                                            android:largeScreens="true"
                                                                                                                                                                                                                                                                                                                                            android:xlargeScreens="true" />
                                                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                                                    Red:

                                                                                                                                                                                                                                                                                                                                    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                        android:versionCode="3001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                        <supports-screens android:smallScreens="false"
                                                                                                                                                                                                                                                                                                                                            android:normalScreens="false"
                                                                                                                                                                                                                                                                                                                                            android:largeScreens="false"
                                                                                                                                                                                                                                                                                                                                            android:xlargeScreens="true" />
                                                                                                                                                                                                                                                                                                                                        ...
                                                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                                                    Note that technically, multiple APK’s will work with either the supports-screens tag, or the compatible-screens tag. Supports-screens is generally preferred, and it’s generally a really bad idea to use both tags in the same manifest. It makes things needlessly complicated, and increases the opportunity for errors. Also note that instead of taking advantage of the default values (small and normal are always true by default), the manifests explicitly set the value for each screen size. This can save you headaches down the line. For instance, a manifest with a target SDK of < 9 will have xlarge automatically set to false, since that size didn’t exist yet. So be explicit!

                                                                                                                                                                                                                                                                                                                                    Go Over Pre-launch Checklist

                                                                                                                                                                                                                                                                                                                                    Before uploading to Google Play, double-check the following items. Remember that these are specifically relevant to multiple APKs, and in no way represent a complete checklist for all applications being uploaded to Google Play.

                                                                                                                                                                                                                                                                                                                                    • All APKs must have the same package name
                                                                                                                                                                                                                                                                                                                                    • All APKs must be signed with the same certificate
                                                                                                                                                                                                                                                                                                                                    • Every screen size you want your APK to support, set to true in the manifest. Every screen size you want it to avoid, set to false
                                                                                                                                                                                                                                                                                                                                    • Double check your manifest filters for conflicting information (an APK that only supports cupcake on XLARGE screens isn’t going to be seen by anybody)
                                                                                                                                                                                                                                                                                                                                    • Each APK's manifest must be unique across at least one of supported screen, openGL texture, or platform version
                                                                                                                                                                                                                                                                                                                                    • Try to test each APK on at least one device. Barring that, you have one of the most customizable device emulators in the business sitting on your development machine. Go nuts!

                                                                                                                                                                                                                                                                                                                                    It’s also worth inspecting the compiled APK before pushing to market, to make sure there aren’t any surprises that could hide your application on Google Play. This is actually quite simple using the "aapt" tool. Aapt (the Android Asset Packaging Tool) is part of the build process for creating and packaging your Android applications, and is also a very handy tool for inspecting them.

                                                                                                                                                                                                                                                                                                                                    >aapt dump badging
                                                                                                                                                                                                                                                                                                                                    package: name='com.example.hello' versionCode='1' versionName='1.0'
                                                                                                                                                                                                                                                                                                                                    sdkVersion:'11'
                                                                                                                                                                                                                                                                                                                                    uses-permission:'android.permission.SEND_SMS'
                                                                                                                                                                                                                                                                                                                                    application-label:'Hello'
                                                                                                                                                                                                                                                                                                                                    application-icon-120:'res/drawable-ldpi/icon.png'
                                                                                                                                                                                                                                                                                                                                    application-icon-160:'res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                    application-icon-240:'res/drawable-hdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                    application: label='Hello' icon='res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                    launchable-activity: name='com.example.hello.HelloActivity'  label='Hello' icon=''
                                                                                                                                                                                                                                                                                                                                    uses-feature:'android.hardware.telephony'
                                                                                                                                                                                                                                                                                                                                    uses-feature:'android.hardware.touchscreen'
                                                                                                                                                                                                                                                                                                                                    main
                                                                                                                                                                                                                                                                                                                                    supports-screens: 'xlarge'
                                                                                                                                                                                                                                                                                                                                    supports-any-density: 'true'
                                                                                                                                                                                                                                                                                                                                    locales: '--_--'
                                                                                                                                                                                                                                                                                                                                    densities: '120' '160' '240'
                                                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                                                    When you examine aapt output, be sure to check that you don’t have conflicting values for supports-screens and compatible-screens, and that you don’t have unintended "uses-feature" values that were added as a result of permissions you set in the manifest. In the example above, the APK will be invisible to most, if not all devices.

                                                                                                                                                                                                                                                                                                                                    Why? By adding the required permission SEND_SMS, the feature requirement of android.hardware.telephony was implicitly added. Since most (if not all) xlarge devices are tablets without telephony hardware in them, Google Play will filter out this APK in these cases, until future devices come along which are both large enough to report as xlarge screen size, and possess telephony hardware.

                                                                                                                                                                                                                                                                                                                                    Fortunately this is easily fixed by adding the following to your manifest:

                                                                                                                                                                                                                                                                                                                                    <uses-feature android:name="android.hardware.telephony" android:required="false" />
                                                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                                                    The android.hardware.touchscreen requirement is also implicitly added. If you want your APK to be visible on TVs which are non-touchscreen devices you should add the following to your manifest:

                                                                                                                                                                                                                                                                                                                                    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
                                                                                                                                                                                                                                                                                                                                    

                                                                                                                                                                                                                                                                                                                                    Once you’ve completed the pre-launch checklist, upload your APKs to Google Play. It may take a bit for the application to show up when browsing Google Play, but when it does, perform one last check. Download the application onto any test devices you may have to make sure that the APKs are targeting the intended devices. Congratulations, you’re done!

                                                                                                                                                                                                                                                                                                                                    This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                                    This class requires API level or higher

                                                                                                                                                                                                                                                                                                                                    This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                                    For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                                    Creating Multiple APKs for Different GL Textures | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                                    Most visited

                                                                                                                                                                                                                                                                                                                                    Recently visited

                                                                                                                                                                                                                                                                                                                                    navigation

                                                                                                                                                                                                                                                                                                                                      Creating Multiple APKs for Different GL Textures

                                                                                                                                                                                                                                                                                                                                      When developing your Android application to take advantage of multiple APKs on Google Play, it’s important to adopt some good practices from the get-go, and prevent unnecessary headaches further into the development process. This lesson shows you how to create multiple APKs of your app, each supporting a different subset of OpenGL texture formats. You will also gain some tools necessary to make maintaining a multiple APK codebase as painless as possible.

                                                                                                                                                                                                                                                                                                                                      Confirm You Need Multiple APKs

                                                                                                                                                                                                                                                                                                                                      When trying to create an application that works across all available Android-powered devices, naturally you want your application look its best on each individual device, regardless of the fact they don’t all support the same set of GL textures. It may seem at the outset as though multiple APK support is the best solution, but this often isn’t the case. The Using Single APK Instead section of the multiple APK developer guide includes some useful information on how to accomplish this with a single APK, including how to detect supported texture formats at runtime. Depending on your situation, it might be easier to bundle all formats with your application, and simply pick which one to use at runtime.

                                                                                                                                                                                                                                                                                                                                      If you can manage it, confining your application to a single APK has several advantages, including:

                                                                                                                                                                                                                                                                                                                                      • Publishing and Testing are easier
                                                                                                                                                                                                                                                                                                                                      • There’s only one codebase to maintain
                                                                                                                                                                                                                                                                                                                                      • Your application can adapt to device configuration changes
                                                                                                                                                                                                                                                                                                                                      • App restore across devices just works
                                                                                                                                                                                                                                                                                                                                      • You don’t have to worry about market preference, behavior from "upgrades" from one APK to the next, or which APK goes with which class of devices

                                                                                                                                                                                                                                                                                                                                      The rest of this lesson assumes that you’ve researched the topic, studiously absorbed the material in the resources linked, and determined that multiple APKs are the right path for your application.

                                                                                                                                                                                                                                                                                                                                      Chart Your Requirements

                                                                                                                                                                                                                                                                                                                                      The Android Developer Guide provides a handy reference of some of common supported textures on the supports-gl-texture page. This page also contains some hints as to which phones (or families of phones) support particular texture formats. Note that it’s generally a good idea for one of your APKs to support ETC1, as that texture format is supported by all Android-powered devices that support the OpenGL ES 2.0 spec.

                                                                                                                                                                                                                                                                                                                                      Since most Android-powered devices support more than one texture format, you need to establish an order of preference. Create a chart including all the formats that your application is going to support. The left-most cell is going to be the lowest priority (It will probably be ETC1, a really solid default in terms of performance and compatibility). Then color in the chart such that each cell represents an APK.

                                                                                                                                                                                                                                                                                                                                      ETC1 ATI PowerVR

                                                                                                                                                                                                                                                                                                                                      Coloring in the chart does more than just make this guide less monochromatic - It also has a way of making intra-team communication easier- You can now simply refer to each APK as "blue", "green", or "red", instead of "The one that supports ETC1 texture formats", etc.

                                                                                                                                                                                                                                                                                                                                      Put All Common Code and Resources in a Library Project

                                                                                                                                                                                                                                                                                                                                      Whether you’re modifying an existing Android application or starting one from scratch, this is the first thing that you should do to the codebase, and by the far the most important. Everything that goes into the library project only needs to be updated once (think language-localized strings, color themes, bugs fixed in shared code), which improves your development time and reduces the likelihood of mistakes that could have been easily avoided.

                                                                                                                                                                                                                                                                                                                                      Note: While the implementation details of how to create and include library projects are beyond the scope of this lesson, you can get up to speed by reading Create an Android Library.

                                                                                                                                                                                                                                                                                                                                      If you’re converting an existing application to use multiple APK support, scour your codebase for every localized string file, list of values, theme colors, menu icons and layout that isn’t going to change across APKs, and put it all in the library project. Code that isn’t going to change much should also go in the library project. You’ll likely find yourself extending these classes to add a method or two from APK to APK.

                                                                                                                                                                                                                                                                                                                                      If, on the other hand, you’re creating the application from scratch, try as much as possible to write code in the library project first, then only move it down to an individual APK if necessary. This is much easier to manage in the long run than adding it to one, then another, then another, then months later trying to figure out whether this blob can be moved up to the library section without screwing anything up.

                                                                                                                                                                                                                                                                                                                                      Create New APK Projects

                                                                                                                                                                                                                                                                                                                                      There should be a separate Android project for each APK you’re going to release. For easy organization, place the library project and all related APK projects under the same parent folder. Also remember that each APK needs to have the same package name, although they don’t necessarily need to share the package name with the library. If you were to have 3 APKs following the scheme described earlier, your root directory might look like this:

                                                                                                                                                                                                                                                                                                                                      alexlucas:~/code/multi-apks-root$ ls
                                                                                                                                                                                                                                                                                                                                      foo-blue
                                                                                                                                                                                                                                                                                                                                      foo-green
                                                                                                                                                                                                                                                                                                                                      foo-lib
                                                                                                                                                                                                                                                                                                                                      foo-red
                                                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                                                      Once the projects are created, add the library project as a reference to each APK project. If possible, define your starting Activity in the library project, and extend that Activity in your APK project. Having a starting activity defined in the library project gives you a chance to put all your application initialization in one place, so that each individual APK doesn’t have to re-implement "universal" tasks like initializing Analytics, running licensing checks, and any other initialization procedures that don’t change much from APK to APK.

                                                                                                                                                                                                                                                                                                                                      Adjust the Manifests

                                                                                                                                                                                                                                                                                                                                      When a user downloads an application which uses multiple APKs through Google Play, the correct APK to use is chosen using some simple rules:

                                                                                                                                                                                                                                                                                                                                      • The manifest has to show that particular APK is eligible
                                                                                                                                                                                                                                                                                                                                      • Of the eligible APKs, highest version number wins
                                                                                                                                                                                                                                                                                                                                      • If any of the texture formats listed in your APK are supported by the device on market, that device is considered eligible

                                                                                                                                                                                                                                                                                                                                      With regards to GL Textures, that last rule is important. It means that you should, for instance, be very careful about using different GL formats in the same application. If you were to use PowerVR 99% of the time, but use ETC1 for, say, your splash screen... Then your manifest would necessarily indicate support for both formats. A device that only supported ETC1 would be deemed compatible, your app would download, and the user would see some thrilling crash messages. The common case is going to be that if you’re using multiple APKs specifically to target different devices based on GL texture support, it’s going to be one texture format per APK.

                                                                                                                                                                                                                                                                                                                                      This actually makes texture support a little bit different than the other two multiple APK dimensions, API level and screen size. Any given device only has one API level, and one screen size, and it’s up to the APK to support a range of them. With textures, the APK will generally support one texture, and the device will support many. There will often be overlap in terms of one device supporting many APKs, but the solution is the same: Version codes.

                                                                                                                                                                                                                                                                                                                                      By way of example, take a few devices, and see how many of the APKs defined earlier fit each device.

                                                                                                                                                                                                                                                                                                                                      FooPhone Nexus S Evo
                                                                                                                                                                                                                                                                                                                                      ETC1 ETC1 ETC1
                                                                                                                                                                                                                                                                                                                                      PowerVR ATI TC

                                                                                                                                                                                                                                                                                                                                      Assuming that PowerVR and ATI formats are both preferred over ETC1 when available, than according to the "highest version number wins" rule, if we set the versionCode attribute in each APK such that red ≥ green ≥ blue, then both Red and Green will always be chosen over Blue on devices which support them, and should a device ever come along which supports both Red and Green, red will be chosen.

                                                                                                                                                                                                                                                                                                                                      In order to keep all your APKs on separate "tracks," it’s important to have a good version code scheme. The recommended one can be found on the Version Codes area of our developer guide. Since the example set of APKs is only dealing with one of 3 possible dimensions, it would be sufficient to separate each APK by 1000 and increment from there. This might look like:

                                                                                                                                                                                                                                                                                                                                      Blue: 1001, 1002, 1003, 1004...
                                                                                                                                                                                                                                                                                                                                      Green: 2001, 2002, 2003, 2004...
                                                                                                                                                                                                                                                                                                                                      Red:3001, 3002, 3003, 3004...

                                                                                                                                                                                                                                                                                                                                      Putting this all together, your Android Manifests would likely look something like the following:

                                                                                                                                                                                                                                                                                                                                      Blue:

                                                                                                                                                                                                                                                                                                                                      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                          android:versionCode="1001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                          <supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
                                                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                                                      Green:

                                                                                                                                                                                                                                                                                                                                      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                          android:versionCode="2001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                          <supports-gl-texture android:name="GL_AMD_compressed_ATC_texture" />
                                                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                                                      Red:

                                                                                                                                                                                                                                                                                                                                      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                          android:versionCode="3001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                          <supports-gl-texture android:name="GL_IMG_texture_compression_pvrtc" />
                                                                                                                                                                                                                                                                                                                                          ...
                                                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                                                      Go Over Pre-launch Checklist

                                                                                                                                                                                                                                                                                                                                      Before uploading to Google Play, double-check the following items. Remember that these are specifically relevant to multiple APKs, and in no way represent a complete checklist for all applications being uploaded to Google Play.

                                                                                                                                                                                                                                                                                                                                      • All APKs must have the same package name
                                                                                                                                                                                                                                                                                                                                      • All APKs must be signed with the same certificate
                                                                                                                                                                                                                                                                                                                                      • Double check your manifest filters for conflicting information (an APK that only supports cupcake on XLARGE screens isn’t going to be seen by anybody)
                                                                                                                                                                                                                                                                                                                                      • Each APK's manifest must be unique across at least one of supported screen, OpenGL texture, or platform version
                                                                                                                                                                                                                                                                                                                                      • Try to test each APK on at least one device. Barring that, you have one of the most customizable device emulators in the business sitting on your development machine. Go nuts!

                                                                                                                                                                                                                                                                                                                                      It’s also worth inspecting the compiled APK before pushing to market, to make sure there aren’t any surprises that could hide your application on Google Play. This is actually quite simple using the "aapt" tool. Aapt (the Android Asset Packaging Tool) is part of the build process for creating and packaging your Android applications, and is also a very handy tool for inspecting them.

                                                                                                                                                                                                                                                                                                                                      >aapt dump badging
                                                                                                                                                                                                                                                                                                                                      package: name='com.example.hello' versionCode='1' versionName='1.0'
                                                                                                                                                                                                                                                                                                                                      sdkVersion:'11'
                                                                                                                                                                                                                                                                                                                                      uses-permission:'android.permission.SEND_SMS'
                                                                                                                                                                                                                                                                                                                                      application-label:'Hello'
                                                                                                                                                                                                                                                                                                                                      application-icon-120:'res/drawable-ldpi/icon.png'
                                                                                                                                                                                                                                                                                                                                      application-icon-160:'res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                      application-icon-240:'res/drawable-hdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                      application: label='Hello' icon='res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                      launchable-activity: name='com.example.hello.HelloActivity'  label='Hello' icon=''
                                                                                                                                                                                                                                                                                                                                      uses-feature:'android.hardware.telephony'
                                                                                                                                                                                                                                                                                                                                      uses-feature:'android.hardware.touchscreen'
                                                                                                                                                                                                                                                                                                                                      main
                                                                                                                                                                                                                                                                                                                                      supports-screens: 'xlarge'
                                                                                                                                                                                                                                                                                                                                      supports-any-density: 'true'
                                                                                                                                                                                                                                                                                                                                      locales: '--_--'
                                                                                                                                                                                                                                                                                                                                      densities: '120' '160' '240'
                                                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                                                      When you examine aapt output, be sure to check that you don’t have conflicting values for supports-screens and compatible-screens, and that you don’t have unintended "uses-feature" values that were added as a result of permissions you set in the manifest. In the example above, the APK will be invisible to most, if not all devices.

                                                                                                                                                                                                                                                                                                                                      Why? By adding the required permission SEND_SMS, the feature requirement of android.hardware.telephony was implicitly added. Since most (if not all) xlarge devices are tablets without telephony hardware in them, Google Play will filter out this APK in these cases, until future devices come along which are both large enough to report as xlarge screen size, and possess telephony hardware.

                                                                                                                                                                                                                                                                                                                                      Fortunately this is easily fixed by adding the following to your manifest:

                                                                                                                                                                                                                                                                                                                                      <uses-feature android:name="android.hardware.telephony" android:required="false" />
                                                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                                                      The android.hardware.touchscreen requirement is also implicitly added. If you want your APK to be visible on TVs which are non-touchscreen devices you should add the following to your manifest:

                                                                                                                                                                                                                                                                                                                                      <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
                                                                                                                                                                                                                                                                                                                                      

                                                                                                                                                                                                                                                                                                                                      Once you’ve completed the pre-launch checklist, upload your APKs to Google Play. It may take a bit for the application to show up when browsing Google Play, but when it does, perform one last check. Download the application onto any test devices you may have to make sure that the APKs are targeting the intended devices. Congratulations, you’re done!

                                                                                                                                                                                                                                                                                                                                      This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                                      This class requires API level or higher

                                                                                                                                                                                                                                                                                                                                      This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                                      For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

                                                                                                                                                                                                                                                                                                                                      Creating Multiple APKs with 2+ Dimensions | Android Developers Skip to content

                                                                                                                                                                                                                                                                                                                                      Most visited

                                                                                                                                                                                                                                                                                                                                      Recently visited

                                                                                                                                                                                                                                                                                                                                      navigation

                                                                                                                                                                                                                                                                                                                                        Creating Multiple APKs with 2+ Dimensions

                                                                                                                                                                                                                                                                                                                                        When developing your Android application to take advantage of multiple APKs on Google Play, it’s important to adopt some good practices from the get-go, and prevent unnecessary headaches further into the development process. This lesson shows you how to create multiple APKs of your app, each covering a different class of screen size. You will also gain some tools necessary to make maintaining a multiple APK codebase as painless as possible.

                                                                                                                                                                                                                                                                                                                                        Confirm You Need Multiple APKs

                                                                                                                                                                                                                                                                                                                                        When trying to create an application that works across the huge range of available Android devices, naturally you want your application look its best on each individual device. You want to take advantage of the space of large screens but still work on small ones, to use new Android API features or visual textures available on cutting edge devices but not abandon older ones. It may seem at the outset as though multiple APK support is the best solution, but this often isn’t the case. The Using Single APK Instead section of the multiple APK guide includes some useful information on how to accomplish all of this with a single APK, including use of our support library, and links to resources throughout the Android Developer guide.

                                                                                                                                                                                                                                                                                                                                        If you can manage it, confining your application to a single APK has several advantages, including:

                                                                                                                                                                                                                                                                                                                                        • Publishing and Testing are easier
                                                                                                                                                                                                                                                                                                                                        • There’s only one codebase to maintain
                                                                                                                                                                                                                                                                                                                                        • Your application can adapt to device configuration changes
                                                                                                                                                                                                                                                                                                                                        • App restore across devices just works
                                                                                                                                                                                                                                                                                                                                        • You don’t have to worry about market preference, behavior from "upgrades" from one APK to the next, or which APK goes with which class of devices

                                                                                                                                                                                                                                                                                                                                        The rest of this lesson assumes that you’ve researched the topic, studiously absorbed the material in the resources linked, and determined that multiple APKs are the right path for your application.

                                                                                                                                                                                                                                                                                                                                        Chart Your Requirements

                                                                                                                                                                                                                                                                                                                                        Start off by creating a simple chart to quickly determine how many APKs you need, and what screen size(s) each APK covers. Fortunately, it’s easy to chart out your requirements quickly, easily, and have an easy reference for later. Let’s say you want to split your APKs across two dimensions, API and screen size. Create a table with a row and column for each possible pair of values, and color in some "blobs", each color representing one APK.

                                                                                                                                                                                                                                                                                                                                        3 4 5 6 7 8 9 10 11 12 +
                                                                                                                                                                                                                                                                                                                                        small
                                                                                                                                                                                                                                                                                                                                        normal
                                                                                                                                                                                                                                                                                                                                        large
                                                                                                                                                                                                                                                                                                                                        xlarge

                                                                                                                                                                                                                                                                                                                                        Above is an example with four APKs. Blue is for all small/normal screen devices, Green is for large screen devices, and Red is for xlarge screen devices, all with an API range of 3-10. Purple is a special case, as it’s for all screen sizes, but only for API 11 and up. More importantly, just by glancing at this chart, you immediately know which APK covers any given API/screen-size combo. To boot, you also have swanky codenames for each one, since "Have we tested red on the ?" is a lot easier to ask your cubie than "Have we tested the 3-to-10 xlarge APK against the Xoom?" Print this chart out and hand it to every person working on your codebase. Life just got a lot easier.

                                                                                                                                                                                                                                                                                                                                        Put All Common Code and Resources in a Library Project

                                                                                                                                                                                                                                                                                                                                        Whether you’re modifying an existing Android application or starting one from scratch, this is the first thing that you should do to the codebase, and by the far the most important. Everything that goes into the library project only needs to be updated once (think language-localized strings, color themes, bugs fixed in shared code), which improves your development time and reduces the likelihood of mistakes that could have been easily avoided.

                                                                                                                                                                                                                                                                                                                                        Note: While the implementation details of how to create and include library projects are beyond the scope of this lesson, you can get up to speed by reading Create an Android Library.

                                                                                                                                                                                                                                                                                                                                        If you’re converting an existing application to use multiple APK support, scour your codebase for every localized string file, list of values, theme colors, menu icons and layout that isn’t going to change across APKs, and put it all in the library project. Code that isn’t going to change much should also go in the library project. You’ll likely find yourself extending these classes to add a method or two from APK to APK.

                                                                                                                                                                                                                                                                                                                                        If, on the other hand, you’re creating the application from scratch, try as much as possible to write code in the library project first, then only move it down to an individual APK if necessary. This is much easier to manage in the long run than adding it to one, then another, then another, then months later trying to figure out whether this blob can be moved up to the library section without screwing anything up.

                                                                                                                                                                                                                                                                                                                                        Create New APK Projects

                                                                                                                                                                                                                                                                                                                                        There should be a separate Android project for each APK you’re going to release. For easy organization, place the library project and all related APK projects under the same parent folder. Also remember that each APK needs to have the same package name, although they don’t necessarily need to share the package name with the library. If you were to have 3 APKs following the scheme described earlier, your root directory might look like this:

                                                                                                                                                                                                                                                                                                                                        alexlucas:~/code/multi-apks-root$ ls
                                                                                                                                                                                                                                                                                                                                        foo-blue
                                                                                                                                                                                                                                                                                                                                        foo-green
                                                                                                                                                                                                                                                                                                                                        foo-lib
                                                                                                                                                                                                                                                                                                                                        foo-purple
                                                                                                                                                                                                                                                                                                                                        foo-red
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        Once the projects are created, add the library project as a reference to each APK project. If possible, define your starting Activity in the library project, and extend that Activity in your APK project. Having a starting activity defined in the library project gives you a chance to put all your application initialization in one place, so that each individual APK doesn’t have to re-implement "universal" tasks like initializing Analytics, running licensing checks, and any other initialization procedures that don’t change much from APK to APK.

                                                                                                                                                                                                                                                                                                                                        Adjust the Manifests

                                                                                                                                                                                                                                                                                                                                        When a user downloads an application which uses multiple APKs through Google Play, the correct APK to use is chosen using two simple rules:

                                                                                                                                                                                                                                                                                                                                        • The manifest has to show that particular APK is eligible
                                                                                                                                                                                                                                                                                                                                        • Of the eligible APKs, highest version number wins.

                                                                                                                                                                                                                                                                                                                                        By way of example, let’s take the set of multiple APKs described earlier, and assume that each APK has been set to support all screen sizes larger than its "target" screen size. Let’s look at the sample chart from earlier:

                                                                                                                                                                                                                                                                                                                                        3 4 5 6 7 8 9 10 11 12 +
                                                                                                                                                                                                                                                                                                                                        small
                                                                                                                                                                                                                                                                                                                                        normal
                                                                                                                                                                                                                                                                                                                                        large
                                                                                                                                                                                                                                                                                                                                        xlarge

                                                                                                                                                                                                                                                                                                                                        Since it’s okay for coverage to overlap, we can describe the area covered by each APK like so:

                                                                                                                                                                                                                                                                                                                                        • Blue covers all screens, minSDK 3.
                                                                                                                                                                                                                                                                                                                                        • Green covers Large screens and higher, minSDK 3.
                                                                                                                                                                                                                                                                                                                                        • Red covers XLarge screens (generally tablets), minSDK of 9.
                                                                                                                                                                                                                                                                                                                                        • Purple covers all screens, minSDK of 11.

                                                                                                                                                                                                                                                                                                                                        Note that there’s a lot of overlap in those rules. For instance, an XLarge device with API 11 can conceivably run any one of the 4 APKs specified. However, by using the "highest version number wins" rule, we can set an order of preference as follows:

                                                                                                                                                                                                                                                                                                                                        Purple ≥ Red ≥ Green ≥ Blue

                                                                                                                                                                                                                                                                                                                                        Why allow all the overlap? Let’s pretend that the Purple APK has some requirement on it that the other two don’t. The Filters on Google Play page of the Android Developer guide has a whole list of possible culprits. For the sake of example, let’s assume that Purple requires a front-facing camera. In fact, the entire point of Purple is to use entertaining things with the front-facing camera! But, it turns out, not all API 11+ devices even HAVE front-facing cameras! The horror!

                                                                                                                                                                                                                                                                                                                                        Fortunately, if a user is browsing Google Play from one such device, Google Play will look at the manifest, see that Purple lists the front-facing camera as a requirement, and quietly ignore it, having determined that Purple and that device are not a match made in digital heaven. It will then see that Red is not only compatible with xlarge devices, but also doesn’t care whether or not there’s a front-facing camera! The app can still be downloaded from Google Play by the user, because despite the whole front-camera mishap, there was still an APK that supported that particular API level.

                                                                                                                                                                                                                                                                                                                                        In order to keep all your APKs on separate "tracks", it’s important to have a good version code scheme. The recommended one can be found on the Version Codes area of our developer guide. It’s worth reading the whole section, but the basic gist is for this set of APKs, we’d use two digits to represent the minSDK, two to represent the min/max screen size, and 3 to represent the build number. That way, when the device upgraded to a new version of Android, (say, from 10 to 11), any APKs that are now eligible and preferred over the currently installed one would be seen by the device as an "upgrade". The version number scheme, when applied to the example set of APKs, might look like:

                                                                                                                                                                                                                                                                                                                                        Blue: 0304001, 0304002, 0304003...
                                                                                                                                                                                                                                                                                                                                        Green: 0334001, 0334002, 0334003
                                                                                                                                                                                                                                                                                                                                        Red: 0344001, 0344002, 0344003...
                                                                                                                                                                                                                                                                                                                                        Purple: 1104001, 1104002, 1104003...

                                                                                                                                                                                                                                                                                                                                        Putting this all together, your Android Manifests would likely look something like the following:

                                                                                                                                                                                                                                                                                                                                        Blue:

                                                                                                                                                                                                                                                                                                                                        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                            android:versionCode="0304001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                            <uses-sdk android:minSdkVersion="3" />
                                                                                                                                                                                                                                                                                                                                            <supports-screens android:smallScreens="true"
                                                                                                                                                                                                                                                                                                                                                android:normalScreens="true"
                                                                                                                                                                                                                                                                                                                                                android:largeScreens="true"
                                                                                                                                                                                                                                                                                                                                                android:xlargeScreens="true" />
                                                                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        Green:

                                                                                                                                                                                                                                                                                                                                        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                            android:versionCode="0334001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                            <uses-sdk android:minSdkVersion="3" />
                                                                                                                                                                                                                                                                                                                                            <supports-screens android:smallScreens="false"
                                                                                                                                                                                                                                                                                                                                                android:normalScreens="false"
                                                                                                                                                                                                                                                                                                                                                android:largeScreens="true"
                                                                                                                                                                                                                                                                                                                                                android:xlargeScreens="true" />
                                                                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        Red:

                                                                                                                                                                                                                                                                                                                                        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                            android:versionCode="0344001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                            <uses-sdk android:minSdkVersion="3" />
                                                                                                                                                                                                                                                                                                                                            <supports-screens android:smallScreens="false"
                                                                                                                                                                                                                                                                                                                                                android:normalScreens="false"
                                                                                                                                                                                                                                                                                                                                                android:largeScreens="false"
                                                                                                                                                                                                                                                                                                                                                android:xlargeScreens="true" />
                                                                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        Purple:

                                                                                                                                                                                                                                                                                                                                        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                                                                                                                                                                                                                                                                                                                                            android:versionCode="1104001" android:versionName="1.0" package="com.example.foo">
                                                                                                                                                                                                                                                                                                                                            <uses-sdk android:minSdkVersion="11" />
                                                                                                                                                                                                                                                                                                                                            <supports-screens android:smallScreens="true"
                                                                                                                                                                                                                                                                                                                                                android:normalScreens="true"
                                                                                                                                                                                                                                                                                                                                                android:largeScreens="true"
                                                                                                                                                                                                                                                                                                                                                android:xlargeScreens="true" />
                                                                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        Note that technically, multiple APK’s will work with either the supports-screens tag, or the compatible-screens tag. Supports-screens is generally preferred, and it’s generally a really bad idea to use both- It makes things needlessly complicated, and increases the opportunity for errors. Also note that instead of taking advantage of the default values (small and normal are always true by default), the manifests explicitly set the value for each screen size. This can save you headaches down the line - By way of example, a manifest with a target SDK of < 9 will have xlarge automatically set to false, since that size didn’t exist yet. So be explicit!

                                                                                                                                                                                                                                                                                                                                        Go Over Pre-launch Checklist

                                                                                                                                                                                                                                                                                                                                        Before uploading to Google Play, double-check the following items. Remember that these are specifically relevant to multiple APKs, and in no way represent a complete checklist for all applications being uploaded to Google Play.

                                                                                                                                                                                                                                                                                                                                        • All APKs must have the same package name.
                                                                                                                                                                                                                                                                                                                                        • All APKs must be signed with the same certificate.
                                                                                                                                                                                                                                                                                                                                        • If the APKs overlap in platform version, the one with the higher minSdkVersion must have a higher version code.
                                                                                                                                                                                                                                                                                                                                        • Every screen size you want your APK to support, set to true in the manifest. Every screen size you want it to avoid, set to false.
                                                                                                                                                                                                                                                                                                                                        • Double check your manifest filters for conflicting information (an APK that only supports cupcake on XLARGE screens isn’t going to be seen by anybody)
                                                                                                                                                                                                                                                                                                                                        • Each APK's manifest must be unique across at least one of supported screen, OpenGL texture, or platform version.
                                                                                                                                                                                                                                                                                                                                        • Try to test each APK on at least one device. Barring that, you have one of the most customizable device emulators in the business sitting on your development machine. Go nuts!

                                                                                                                                                                                                                                                                                                                                        It’s also worth inspecting the compiled APK before pushing to market, to make sure there aren’t any surprises that could hide your application on Google Play. This is actually quite simple using the "aapt" tool. Aapt (the Android Asset Packaging Tool) is part of the build process for creating and packaging your Android applications, and is also a very handy tool for inspecting them.

                                                                                                                                                                                                                                                                                                                                        >aapt dump badging
                                                                                                                                                                                                                                                                                                                                        package: name='com.example.hello' versionCode='1' versionName='1.0'
                                                                                                                                                                                                                                                                                                                                        sdkVersion:'11'
                                                                                                                                                                                                                                                                                                                                        uses-permission:'android.permission.SEND_SMS'
                                                                                                                                                                                                                                                                                                                                        application-label:'Hello'
                                                                                                                                                                                                                                                                                                                                        application-icon-120:'res/drawable-ldpi/icon.png'
                                                                                                                                                                                                                                                                                                                                        application-icon-160:'res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                        application-icon-240:'res/drawable-hdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                        application: label='Hello' icon='res/drawable-mdpi/icon.png'
                                                                                                                                                                                                                                                                                                                                        launchable-activity: name='com.example.hello.HelloActivity'  label='Hello' icon=''
                                                                                                                                                                                                                                                                                                                                        uses-feature:'android.hardware.telephony'
                                                                                                                                                                                                                                                                                                                                        uses-feature:'android.hardware.touchscreen'
                                                                                                                                                                                                                                                                                                                                        main
                                                                                                                                                                                                                                                                                                                                        supports-screens: 'xlarge'
                                                                                                                                                                                                                                                                                                                                        supports-any-density: 'true'
                                                                                                                                                                                                                                                                                                                                        locales: '--_--'
                                                                                                                                                                                                                                                                                                                                        densities: '120' '160' '240'
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        When you examine aapt output, be sure to check that you don’t have conflicting values for supports-screens and compatible-screens, and that you don’t have unintended "uses-feature" values that were added as a result of permissions you set in the manifest. In the example above, the APK will be invisible to most, if not all devices.

                                                                                                                                                                                                                                                                                                                                        Why? By adding the required permission SEND_SMS, the feature requirement of android.hardware.telephony was implicitly added. Since most (if not all) xlarge devices are tablets without telephony hardware in them, Google Play will filter out this APK in these cases, until future devices come along which are both large enough to report as xlarge screen size, and possess telephony hardware.

                                                                                                                                                                                                                                                                                                                                        Fortunately this is easily fixed by adding the following to your manifest:

                                                                                                                                                                                                                                                                                                                                        <uses-feature android:name="android.hardware.telephony" android:required="false" />
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        The android.hardware.touchscreen requirement is also implicitly added. If you want your APK to be visible on TVs which are non-touchscreen devices you should add the following to your manifest:

                                                                                                                                                                                                                                                                                                                                        <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
                                                                                                                                                                                                                                                                                                                                        

                                                                                                                                                                                                                                                                                                                                        Once you’ve completed the pre-launch checklist, upload your APKs to Google Play. It may take a bit for the application to show up when browsing Google Play, but when it does, perform one last check. Download the application onto any test devices you may have to make sure that the APKs are targeting the intended devices. Congratulations, you’re done!

                                                                                                                                                                                                                                                                                                                                        This site uses cookies to store your preferences for site-specific language and display options.

                                                                                                                                                                                                                                                                                                                                        This class requires API level or higher

                                                                                                                                                                                                                                                                                                                                        This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

                                                                                                                                                                                                                                                                                                                                        For more information about specifying the API level your app requires, read Supporting Different Platform Versions.